object-0.12.0/.gitignore010066400017500001750000000000221330611603300132410ustar0000000000000000target Cargo.lock object-0.12.0/Cargo.toml.orig010066400017500001750000000016701346047733500141720ustar0000000000000000[package] authors = ["Nick Fitzgerald ", "Philip Craig "] name = "object" version = "0.12.0" edition = "2018" description = "A unified interface for parsing object file formats." keywords = ["object", "loader", "elf", "mach-o", "pe"] license = "Apache-2.0/MIT" repository = "https://github.com/gimli-rs/object" exclude = ["/.coveralls.yml", "/.travis.yml"] [dependencies] scroll = { version = "0.9", default-features = false } uuid = { version = "0.7", default-features = false } flate2 = { version = "1", optional = true } [dependencies.goblin] version = "0.0.22" default-features = false features = ["endian_fd", "elf32", "elf64", "mach32", "mach64", "pe32", "pe64", "archive"] [dependencies.parity-wasm] version = "0.38.0" optional = true [dev-dependencies] memmap = "0.7" [features] std = ["goblin/std"] compression = ["flate2"] wasm = ["std", "parity-wasm"] default = ["std", "compression", "wasm"] object-0.12.0/Cargo.toml0000644000000027260000000000000104260ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "object" version = "0.12.0" authors = ["Nick Fitzgerald ", "Philip Craig "] exclude = ["/.coveralls.yml", "/.travis.yml"] description = "A unified interface for parsing object file formats." keywords = ["object", "loader", "elf", "mach-o", "pe"] license = "Apache-2.0/MIT" repository = "https://github.com/gimli-rs/object" [dependencies.flate2] version = "1" optional = true [dependencies.goblin] version = "0.0.22" features = ["endian_fd", "elf32", "elf64", "mach32", "mach64", "pe32", "pe64", "archive"] default-features = false [dependencies.parity-wasm] version = "0.38.0" optional = true [dependencies.scroll] version = "0.9" default-features = false [dependencies.uuid] version = "0.7" default-features = false [dev-dependencies.memmap] version = "0.7" [features] compression = ["flate2"] default = ["std", "compression", "wasm"] std = ["goblin/std"] wasm = ["std", "parity-wasm"] object-0.12.0/LICENSE-APACHE010066400017500001750000000251371330611603300132130ustar0000000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. object-0.12.0/LICENSE-MIT010066400017500001750000000020501330611603300127100ustar0000000000000000Copyright (c) 2015 The Gimli Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. object-0.12.0/README.md010066400017500001750000000001311330611603300125310ustar0000000000000000# `object` A library for parsing object files across platforms with a single interface. object-0.12.0/examples/nm.rs010066400017500001750000000052051346047733500140770ustar0000000000000000use object::{Object, ObjectSection, SectionIndex, SectionKind, Symbol, SymbolKind}; use std::collections::HashMap; use std::{env, fs, process}; fn main() { let arg_len = env::args().len(); if arg_len <= 1 { eprintln!("Usage: {} ...", env::args().next().unwrap()); process::exit(1); } for file_path in env::args().skip(1) { if arg_len > 2 { println!(); println!("{}:", file_path); } let file = match fs::File::open(&file_path) { Ok(file) => file, Err(err) => { println!("Failed to open file '{}': {}", file_path, err,); continue; } }; let file = match unsafe { memmap::Mmap::map(&file) } { Ok(mmap) => mmap, Err(err) => { println!("Failed to map file '{}': {}", file_path, err,); continue; } }; let file = match object::File::parse(&*file) { Ok(file) => file, Err(err) => { println!("Failed to parse file '{}': {}", file_path, err); continue; } }; let section_kinds = file.sections().map(|s| (s.index(), s.kind())).collect(); println!("Debugging symbols:"); for (_, symbol) in file.symbols() { print_symbol(&symbol, §ion_kinds); } println!(); println!("Dynamic symbols:"); for (_, symbol) in file.dynamic_symbols() { print_symbol(&symbol, §ion_kinds); } } } fn print_symbol(symbol: &Symbol<'_>, section_kinds: &HashMap) { if let SymbolKind::Section | SymbolKind::File = symbol.kind() { return; } let mut kind = match symbol .section_index() .and_then(|index| section_kinds.get(&index)) { Some(SectionKind::Unknown) | Some(SectionKind::Other) | Some(SectionKind::OtherString) | Some(SectionKind::Metadata) => '?', Some(SectionKind::Text) => 't', Some(SectionKind::Data) | Some(SectionKind::Tls) => 'd', Some(SectionKind::ReadOnlyData) | Some(SectionKind::ReadOnlyString) => 'r', Some(SectionKind::UninitializedData) | Some(SectionKind::UninitializedTls) => 'b', None => 'U', }; if symbol.is_global() { kind = kind.to_ascii_uppercase(); } if symbol.is_undefined() { print!("{:16} ", ""); } else { print!("{:016x} ", symbol.address()); } println!( "{:016x} {} {}", symbol.size(), kind, symbol.name().unwrap_or(""), ); } object-0.12.0/examples/objdump.rs010066400017500001750000000043351346047733500151300ustar0000000000000000use object::{Object, ObjectSection}; use std::{env, fs, process}; fn main() { let arg_len = env::args().len(); if arg_len <= 1 { eprintln!("Usage: {} ...", env::args().next().unwrap()); process::exit(1); } for file_path in env::args().skip(1) { if arg_len > 2 { println!(); println!("{}:", file_path); } let file = match fs::File::open(&file_path) { Ok(file) => file, Err(err) => { println!("Failed to open file '{}': {}", file_path, err,); return; } }; let file = match unsafe { memmap::Mmap::map(&file) } { Ok(mmap) => mmap, Err(err) => { println!("Failed to map file '{}': {}", file_path, err,); return; } }; let file = match object::File::parse(&*file) { Ok(file) => file, Err(err) => { println!("Failed to parse file '{}': {}", file_path, err); return; } }; if let Some(uuid) = file.mach_uuid() { println!("Mach UUID: {}", uuid); } if let Some(build_id) = file.build_id() { println!("Build ID: {:x?}", build_id); } if let Some((filename, crc)) = file.gnu_debuglink() { println!( "GNU debug link: {} CRC: {:08x}", String::from_utf8_lossy(filename), crc ); } for segment in file.segments() { println!("{:?}", segment); } for (index, section) in file.sections().enumerate() { println!("{}: {:?}", index, section); } for (index, symbol) in file.symbols() { println!("{}: {:?}", index.0, symbol); } for section in file.sections() { if section.relocations().next().is_some() { println!( "\n{} relocations", section.name().unwrap_or("") ); for relocation in section.relocations() { println!("{:?}", relocation); } } } } } object-0.12.0/src/elf.rs010066400017500001750000000534551346047733500132160ustar0000000000000000use crate::alloc::borrow::Cow; use crate::alloc::fmt; use crate::alloc::vec::Vec; use std::iter; use std::slice; #[cfg(feature = "compression")] use flate2::{Decompress, FlushDecompress}; #[cfg(feature = "compression")] use goblin::container; use goblin::{elf, strtab}; #[cfg(feature = "compression")] use scroll::ctx::TryFromCtx; use scroll::{self, Pread}; use crate::{ Machine, Object, ObjectSection, ObjectSegment, Relocation, RelocationKind, SectionIndex, SectionKind, Symbol, SymbolIndex, SymbolKind, SymbolMap, }; /// An ELF object file. #[derive(Debug)] pub struct ElfFile<'data> { elf: elf::Elf<'data>, data: &'data [u8], } /// An iterator over the segments of an `ElfFile`. #[derive(Debug)] pub struct ElfSegmentIterator<'data, 'file> where 'data: 'file, { file: &'file ElfFile<'data>, iter: slice::Iter<'file, elf::ProgramHeader>, } /// A segment of an `ElfFile`. #[derive(Debug)] pub struct ElfSegment<'data, 'file> where 'data: 'file, { file: &'file ElfFile<'data>, segment: &'file elf::ProgramHeader, } /// An iterator over the sections of an `ElfFile`. #[derive(Debug)] pub struct ElfSectionIterator<'data, 'file> where 'data: 'file, { file: &'file ElfFile<'data>, iter: iter::Enumerate>, } /// A section of an `ElfFile`. #[derive(Debug)] pub struct ElfSection<'data, 'file> where 'data: 'file, { file: &'file ElfFile<'data>, index: SectionIndex, section: &'file elf::SectionHeader, } /// An iterator over the symbols of an `ElfFile`. pub struct ElfSymbolIterator<'data, 'file> where 'data: 'file, { strtab: &'file strtab::Strtab<'data>, symbols: iter::Enumerate>, } /// An iterator over the relocations in an `ElfSection`. pub struct ElfRelocationIterator<'data, 'file> where 'data: 'file, { /// The index of the section that the relocations apply to. section_index: SectionIndex, file: &'file ElfFile<'data>, sections: slice::Iter<'file, (elf::ShdrIdx, elf::RelocSection<'data>)>, relocations: Option>, } impl<'data> ElfFile<'data> { /// Get the ELF headers of the file. // TODO: this is temporary to allow access to features this crate doesn't provide yet #[inline] pub fn elf(&self) -> &elf::Elf<'data> { &self.elf } /// Parse the raw ELF file data. pub fn parse(data: &'data [u8]) -> Result { let elf = elf::Elf::parse(data).map_err(|_| "Could not parse ELF header")?; Ok(ElfFile { elf, data }) } /// True for 64-bit files. #[inline] pub fn is_64(&self) -> bool { self.elf.is_64 } fn raw_section_by_name<'file>( &'file self, section_name: &str, ) -> Option> { for (index, section) in self.elf.section_headers.iter().enumerate() { if let Some(Ok(name)) = self.elf.shdr_strtab.get(section.sh_name) { if name == section_name { return Some(ElfSection { file: self, index: SectionIndex(index), section, }); } } } None } #[cfg(feature = "compression")] fn zdebug_section_by_name<'file>( &'file self, section_name: &str, ) -> Option> { if !section_name.starts_with(".debug_") { return None; } self.raw_section_by_name(&format!(".zdebug_{}", §ion_name[7..])) } #[cfg(not(feature = "compression"))] fn zdebug_section_by_name<'file>( &'file self, _section_name: &str, ) -> Option> { None } } impl<'data, 'file> Object<'data, 'file> for ElfFile<'data> where 'data: 'file, { type Segment = ElfSegment<'data, 'file>; type SegmentIterator = ElfSegmentIterator<'data, 'file>; type Section = ElfSection<'data, 'file>; type SectionIterator = ElfSectionIterator<'data, 'file>; type SymbolIterator = ElfSymbolIterator<'data, 'file>; fn machine(&self) -> Machine { match self.elf.header.e_machine { elf::header::EM_ARM => Machine::Arm, elf::header::EM_AARCH64 => Machine::Arm64, elf::header::EM_386 => Machine::X86, elf::header::EM_X86_64 => Machine::X86_64, elf::header::EM_MIPS => Machine::Mips, _ => Machine::Other, } } fn segments(&'file self) -> ElfSegmentIterator<'data, 'file> { ElfSegmentIterator { file: self, iter: self.elf.program_headers.iter(), } } fn section_by_name(&'file self, section_name: &str) -> Option> { self.raw_section_by_name(section_name) .or_else(|| self.zdebug_section_by_name(section_name)) } fn section_by_index(&'file self, index: SectionIndex) -> Option> { self.elf .section_headers .get(index.0) .map(|section| ElfSection { file: self, index, section, }) } fn sections(&'file self) -> ElfSectionIterator<'data, 'file> { ElfSectionIterator { file: self, iter: self.elf.section_headers.iter().enumerate(), } } fn symbol_by_index(&self, index: SymbolIndex) -> Option> { self.elf .syms .get(index.0) .map(|symbol| parse_symbol(index.0, &symbol, &self.elf.strtab)) } fn symbols(&'file self) -> ElfSymbolIterator<'data, 'file> { ElfSymbolIterator { strtab: &self.elf.strtab, symbols: self.elf.syms.iter().enumerate(), } } fn dynamic_symbols(&'file self) -> ElfSymbolIterator<'data, 'file> { ElfSymbolIterator { strtab: &self.elf.dynstrtab, symbols: self.elf.dynsyms.iter().enumerate(), } } fn symbol_map(&self) -> SymbolMap<'data> { let mut symbols: Vec<_> = self .symbols() .map(|(_, s)| s) .filter(SymbolMap::filter) .collect(); symbols.sort_by_key(|x| x.address); SymbolMap { symbols } } #[inline] fn is_little_endian(&self) -> bool { self.elf.little_endian } fn has_debug_symbols(&self) -> bool { for header in &self.elf.section_headers { if let Some(Ok(name)) = self.elf.shdr_strtab.get(header.sh_name) { if name == ".debug_info" || name == ".zdebug_info" { return true; } } } false } fn build_id(&self) -> Option<&'data [u8]> { if let Some(mut notes) = self.elf.iter_note_headers(self.data) { while let Some(Ok(note)) = notes.next() { if note.n_type == elf::note::NT_GNU_BUILD_ID { return Some(note.desc); } } } if let Some(mut notes) = self .elf .iter_note_sections(self.data, Some(".note.gnu.build-id")) { while let Some(Ok(note)) = notes.next() { if note.n_type == elf::note::NT_GNU_BUILD_ID { return Some(note.desc); } } } None } fn gnu_debuglink(&self) -> Option<(&'data [u8], u32)> { if let Some(Cow::Borrowed(data)) = self.section_data_by_name(".gnu_debuglink") { if let Some(filename_len) = data.iter().position(|x| *x == 0) { let filename = &data[..filename_len]; // Round to 4 byte alignment after null terminator. let offset = (filename_len + 1 + 3) & !3; if offset + 4 <= data.len() { let endian = if self.is_little_endian() { scroll::LE } else { scroll::BE }; let crc: u32 = data.pread_with(offset, endian).unwrap(); return Some((filename, crc)); } } } None } fn entry(&self) -> u64 { self.elf.entry } } impl<'data, 'file> Iterator for ElfSegmentIterator<'data, 'file> { type Item = ElfSegment<'data, 'file>; fn next(&mut self) -> Option { while let Some(segment) = self.iter.next() { if segment.p_type == elf::program_header::PT_LOAD { return Some(ElfSegment { file: self.file, segment, }); } } None } } impl<'data, 'file> ObjectSegment<'data> for ElfSegment<'data, 'file> { #[inline] fn address(&self) -> u64 { self.segment.p_vaddr } #[inline] fn size(&self) -> u64 { self.segment.p_memsz } #[inline] fn align(&self) -> u64 { self.segment.p_align } fn data(&self) -> &'data [u8] { &self.file.data[self.segment.p_offset as usize..][..self.segment.p_filesz as usize] } fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]> { crate::data_range(self.data(), self.address(), address, size) } #[inline] fn name(&self) -> Option<&str> { None } } impl<'data, 'file> Iterator for ElfSectionIterator<'data, 'file> { type Item = ElfSection<'data, 'file>; fn next(&mut self) -> Option { self.iter.next().map(|(index, section)| ElfSection { index: SectionIndex(index), file: self.file, section, }) } } impl<'data, 'file> ElfSection<'data, 'file> { fn raw_data(&self) -> &'data [u8] { if self.section.sh_type == elf::section_header::SHT_NOBITS { &[] } else { &self.file.data[self.section.sh_offset as usize..][..self.section.sh_size as usize] } } #[cfg(feature = "compression")] fn maybe_decompress_data(&self) -> Option> { if (self.section.sh_flags & u64::from(elf::section_header::SHF_COMPRESSED)) == 0 { return None; } let container = match self.file.elf.header.container() { Ok(c) => c, Err(_) => return None, }; let endianness = match self.file.elf.header.endianness() { Ok(e) => e, Err(_) => return None, }; let ctx = container::Ctx::new(container, endianness); let data = self.raw_data(); let (compression_type, uncompressed_size, compressed_data) = match elf::compression_header::CompressionHeader::try_from_ctx(&data, ctx) { Ok((chdr, size)) => (chdr.ch_type, chdr.ch_size, &data[size..]), Err(_) => return None, }; if compression_type != elf::compression_header::ELFCOMPRESS_ZLIB { return None; } let mut decompressed = Vec::with_capacity(uncompressed_size as usize); let mut decompress = Decompress::new(true); if decompress .decompress_vec(compressed_data, &mut decompressed, FlushDecompress::Finish) .is_err() { return None; } Some(Cow::Owned(decompressed)) } /// Try GNU-style "ZLIB" header decompression. #[cfg(feature = "compression")] fn maybe_decompress_data_gnu(&self) -> Option> { let name = match self.name() { Some(name) => name, None => return None, }; if !name.starts_with(".zdebug_") { return None; } let data = self.raw_data(); // Assume ZLIB-style uncompressed data is no more than 4GB to avoid accidentally // huge allocations. This also reduces the chance of accidentally matching on a // .debug_str that happens to start with "ZLIB". if data.len() < 12 || &data[..8] != b"ZLIB\0\0\0\0" { return None; } let uncompressed_size: u32 = data.pread_with(8, scroll::BE).unwrap(); let mut decompressed = Vec::with_capacity(uncompressed_size as usize); let mut decompress = Decompress::new(true); if decompress .decompress_vec(&data[12..], &mut decompressed, FlushDecompress::Finish) .is_err() { return None; } Some(Cow::Owned(decompressed)) } } impl<'data, 'file> ObjectSection<'data> for ElfSection<'data, 'file> { type RelocationIterator = ElfRelocationIterator<'data, 'file>; #[inline] fn index(&self) -> SectionIndex { self.index } #[inline] fn address(&self) -> u64 { self.section.sh_addr } #[inline] fn size(&self) -> u64 { self.section.sh_size } #[inline] fn align(&self) -> u64 { self.section.sh_addralign } #[inline] fn data(&self) -> Cow<'data, [u8]> { Cow::from(self.raw_data()) } fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]> { crate::data_range(self.raw_data(), self.address(), address, size) } #[cfg(feature = "compression")] fn uncompressed_data(&self) -> Cow<'data, [u8]> { self.maybe_decompress_data() .or_else(|| self.maybe_decompress_data_gnu()) .unwrap_or_else(|| self.data()) } #[cfg(not(feature = "compression"))] #[inline] fn uncompressed_data(&self) -> Cow<'data, [u8]> { self.data() } fn name(&self) -> Option<&str> { self.file .elf .shdr_strtab .get(self.section.sh_name) .and_then(Result::ok) } #[inline] fn segment_name(&self) -> Option<&str> { None } fn kind(&self) -> SectionKind { match self.section.sh_type { elf::section_header::SHT_PROGBITS => { if self.section.sh_flags & u64::from(elf::section_header::SHF_ALLOC) != 0 { if self.section.sh_flags & u64::from(elf::section_header::SHF_EXECINSTR) != 0 { SectionKind::Text } else if self.section.sh_flags & u64::from(elf::section_header::SHF_TLS) != 0 { SectionKind::Tls } else if self.section.sh_flags & u64::from(elf::section_header::SHF_WRITE) != 0 { SectionKind::Data } else if self.section.sh_flags & u64::from(elf::section_header::SHF_STRINGS) != 0 { SectionKind::ReadOnlyString } else { SectionKind::ReadOnlyData } } else if self.section.sh_flags & u64::from(elf::section_header::SHF_STRINGS) != 0 { SectionKind::OtherString } else { SectionKind::Other } } elf::section_header::SHT_NOBITS => { if self.section.sh_flags & u64::from(elf::section_header::SHF_TLS) != 0 { SectionKind::UninitializedTls } else { SectionKind::UninitializedData } } elf::section_header::SHT_NULL | elf::section_header::SHT_SYMTAB | elf::section_header::SHT_STRTAB | elf::section_header::SHT_RELA | elf::section_header::SHT_HASH | elf::section_header::SHT_DYNAMIC | elf::section_header::SHT_REL | elf::section_header::SHT_DYNSYM => SectionKind::Metadata, _ => { // TODO: maybe add more specialised kinds based on sh_type (e.g. Unwind) SectionKind::Unknown } } } fn relocations(&self) -> ElfRelocationIterator<'data, 'file> { ElfRelocationIterator { section_index: self.index, file: self.file, sections: self.file.elf.shdr_relocs.iter(), relocations: None, } } } impl<'data, 'file> fmt::Debug for ElfSymbolIterator<'data, 'file> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ElfSymbolIterator").finish() } } impl<'data, 'file> Iterator for ElfSymbolIterator<'data, 'file> { type Item = (SymbolIndex, Symbol<'data>); fn next(&mut self) -> Option { self.symbols.next().map(|(index, symbol)| { ( SymbolIndex(index), parse_symbol(index, &symbol, self.strtab), ) }) } } fn parse_symbol<'data>( index: usize, symbol: &elf::sym::Sym, strtab: &strtab::Strtab<'data>, ) -> Symbol<'data> { let name = strtab.get(symbol.st_name).and_then(Result::ok); let kind = match elf::sym::st_type(symbol.st_info) { elf::sym::STT_NOTYPE if index == 0 => SymbolKind::Null, elf::sym::STT_OBJECT => SymbolKind::Data, elf::sym::STT_FUNC => SymbolKind::Text, elf::sym::STT_SECTION => SymbolKind::Section, elf::sym::STT_FILE => SymbolKind::File, elf::sym::STT_COMMON => SymbolKind::Common, elf::sym::STT_TLS => SymbolKind::Tls, _ => SymbolKind::Unknown, }; let undefined = symbol.st_shndx == elf::section_header::SHN_UNDEF as usize; let section_index = if undefined || symbol.st_shndx >= elf::section_header::SHN_LORESERVE as usize { None } else { Some(SectionIndex(symbol.st_shndx)) }; Symbol { name, address: symbol.st_value, size: symbol.st_size, kind, section_index, undefined, global: elf::sym::st_bind(symbol.st_info) != elf::sym::STB_LOCAL, } } impl<'data, 'file> Iterator for ElfRelocationIterator<'data, 'file> { type Item = (u64, Relocation); fn next(&mut self) -> Option { loop { if let Some(ref mut relocations) = self.relocations { if let Some(reloc) = relocations.next() { let (kind, size) = match self.file.elf.header.e_machine { elf::header::EM_ARM => match reloc.r_type { elf::reloc::R_ARM_ABS32 => (RelocationKind::Absolute, 32), _ => (RelocationKind::Other(reloc.r_type), 0), }, elf::header::EM_AARCH64 => match reloc.r_type { elf::reloc::R_AARCH64_ABS64 => (RelocationKind::Absolute, 64), elf::reloc::R_AARCH64_ABS32 => (RelocationKind::Absolute, 32), elf::reloc::R_AARCH64_ABS16 => (RelocationKind::Absolute, 16), elf::reloc::R_AARCH64_PREL64 => (RelocationKind::Relative, 64), elf::reloc::R_AARCH64_PREL32 => (RelocationKind::Relative, 32), elf::reloc::R_AARCH64_PREL16 => (RelocationKind::Relative, 16), _ => (RelocationKind::Other(reloc.r_type), 0), }, elf::header::EM_386 => match reloc.r_type { elf::reloc::R_386_32 => (RelocationKind::Absolute, 32), elf::reloc::R_386_PC32 => (RelocationKind::Relative, 32), elf::reloc::R_386_GOT32 => (RelocationKind::GotRelative, 32), elf::reloc::R_386_PLT32 => (RelocationKind::PltRelative, 32), _ => (RelocationKind::Other(reloc.r_type), 0), }, elf::header::EM_X86_64 => match reloc.r_type { elf::reloc::R_X86_64_64 => (RelocationKind::Absolute, 64), elf::reloc::R_X86_64_PC32 => (RelocationKind::Relative, 32), elf::reloc::R_X86_64_GOT32 => (RelocationKind::GotOffset, 32), elf::reloc::R_X86_64_PLT32 => (RelocationKind::PltRelative, 32), elf::reloc::R_X86_64_GOTPCREL => (RelocationKind::GotRelative, 32), elf::reloc::R_X86_64_32 => (RelocationKind::Absolute, 32), elf::reloc::R_X86_64_32S => (RelocationKind::AbsoluteSigned, 32), elf::reloc::R_X86_64_16 => (RelocationKind::Absolute, 16), elf::reloc::R_X86_64_PC16 => (RelocationKind::Relative, 16), elf::reloc::R_X86_64_8 => (RelocationKind::Absolute, 8), elf::reloc::R_X86_64_PC8 => (RelocationKind::Relative, 8), _ => (RelocationKind::Other(reloc.r_type), 0), }, _ => (RelocationKind::Other(reloc.r_type), 0), }; return Some(( reloc.r_offset, Relocation { kind, size, symbol: SymbolIndex(reloc.r_sym as usize), addend: reloc.r_addend.unwrap_or(0), implicit_addend: reloc.r_addend.is_none(), }, )); } } match self.sections.next() { None => return None, Some((index, relocs)) => { let section = &self.file.elf.section_headers[*index]; if section.sh_info as usize == self.section_index.0 { self.relocations = Some(relocs.into_iter()); } // TODO: do we need to return section.sh_link? } } } } } impl<'data, 'file> fmt::Debug for ElfRelocationIterator<'data, 'file> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ElfRelocationIterator").finish() } } object-0.12.0/src/lib.rs010066400017500001750000000566131346047733500132150ustar0000000000000000//! # `object` //! //! The `object` crate provides a unified interface to working with object files //! across platforms. //! //! See the [`File` struct](./struct.File.html) for details. #![deny(missing_docs)] #![deny(missing_debug_implementations)] #![no_std] #![cfg_attr(not(feature = "std"), feature(alloc))] #[cfg(feature = "std")] #[macro_use] extern crate std; #[cfg(all(not(feature = "std"), feature = "compression"))] #[macro_use] extern crate alloc; #[cfg(all(not(feature = "std"), not(feature = "compression")))] extern crate alloc; #[cfg(not(feature = "std"))] extern crate core as std; #[cfg(feature = "std")] mod alloc { pub use std::borrow; pub use std::fmt; pub use std::vec; } use crate::alloc::borrow::Cow; use crate::alloc::fmt; use crate::alloc::vec::Vec; mod elf; pub use crate::elf::*; mod macho; pub use crate::macho::*; mod pe; pub use crate::pe::*; mod traits; pub use crate::traits::*; #[cfg(feature = "wasm")] mod wasm; #[cfg(feature = "wasm")] pub use crate::wasm::*; pub use uuid::Uuid; /// The native object file for the target platform. #[cfg(target_os = "linux")] pub type NativeFile<'data> = ElfFile<'data>; /// The native object file for the target platform. #[cfg(target_os = "macos")] pub type NativeFile<'data> = MachOFile<'data>; /// The native object file for the target platform. #[cfg(target_os = "windows")] pub type NativeFile<'data> = PeFile<'data>; /// The native object file for the target platform. #[cfg(all(feature = "wasm", target_arch = "wasm32"))] pub type NativeFile<'data> = WasmFile<'data>; /// The object file format. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Format { /// 32-bit ELF Elf32, /// 64-bit ELF Elf64, /// 32-bit Mach-O MachO32, /// 64-bit Mach-O MachO64, /// 32-bit PE Pe32, /// 64-bit PE Pe64, /// WebAssembly #[cfg(feature = "wasm")] Wasm, } /// An object file. #[derive(Debug)] pub struct File<'data> { inner: FileInternal<'data>, } #[allow(clippy::large_enum_variant)] #[derive(Debug)] enum FileInternal<'data> { Elf(ElfFile<'data>), MachO(MachOFile<'data>), Pe(PeFile<'data>), #[cfg(feature = "wasm")] Wasm(WasmFile), } /// The machine type of an object file. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Machine { /// An unrecognized machine type. Other, /// ARM Arm, /// ARM64 Arm64, /// x86 X86, /// x86-64 #[allow(non_camel_case_types)] X86_64, /// MIPS Mips, } /// An iterator over the segments of a `File`. #[derive(Debug)] pub struct SegmentIterator<'data, 'file> where 'data: 'file, { inner: SegmentIteratorInternal<'data, 'file>, } #[derive(Debug)] enum SegmentIteratorInternal<'data, 'file> where 'data: 'file, { Elf(ElfSegmentIterator<'data, 'file>), MachO(MachOSegmentIterator<'data, 'file>), Pe(PeSegmentIterator<'data, 'file>), #[cfg(feature = "wasm")] Wasm(WasmSegmentIterator<'file>), } /// A segment of a `File`. pub struct Segment<'data, 'file> where 'data: 'file, { inner: SegmentInternal<'data, 'file>, } #[derive(Debug)] enum SegmentInternal<'data, 'file> where 'data: 'file, { Elf(ElfSegment<'data, 'file>), MachO(MachOSegment<'data, 'file>), Pe(PeSegment<'data, 'file>), #[cfg(feature = "wasm")] Wasm(WasmSegment<'file>), } /// An iterator of the sections of a `File`. #[derive(Debug)] pub struct SectionIterator<'data, 'file> where 'data: 'file, { inner: SectionIteratorInternal<'data, 'file>, } // we wrap our enums in a struct so that they are kept private. #[derive(Debug)] enum SectionIteratorInternal<'data, 'file> where 'data: 'file, { Elf(ElfSectionIterator<'data, 'file>), MachO(MachOSectionIterator<'data, 'file>), Pe(PeSectionIterator<'data, 'file>), #[cfg(feature = "wasm")] Wasm(WasmSectionIterator<'file>), } /// The index used to identify a section of a file. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct SectionIndex(pub usize); /// A Section of a File pub struct Section<'data, 'file> where 'data: 'file, { inner: SectionInternal<'data, 'file>, } enum SectionInternal<'data, 'file> where 'data: 'file, { Elf(ElfSection<'data, 'file>), MachO(MachOSection<'data, 'file>), Pe(PeSection<'data, 'file>), #[cfg(feature = "wasm")] Wasm(WasmSection<'file>), } /// The kind of a section. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SectionKind { /// The section kind is unknown. Unknown, /// An executable code section. /// /// Example ELF sections: `.text` Text, /// A data section. /// /// Example ELF sections: `.data` Data, /// A read only data section. /// /// Example ELF sections: `.rodata` ReadOnlyData, /// A loadable string section. /// /// Example ELF sections: `.rodata.str` ReadOnlyString, /// An uninitialized data section. /// /// Example ELF sections: `.bss` UninitializedData, /// A TLS data section. /// /// Example ELF sections: `.tdata` Tls, /// An uninitialized TLS data section. /// /// Example ELF sections: `.tbss` UninitializedTls, /// A non-loadable string section. /// /// Example ELF sections: `.comment`, `.debug_str` OtherString, /// Some other non-loadable section. /// /// Example ELF sections: `.debug_info` Other, /// Metadata such as symbols or relocations. /// /// Example ELF sections: `.symtab`, `.strtab` Metadata, } /// An iterator over symbol table entries. #[derive(Debug)] pub struct SymbolIterator<'data, 'file> where 'data: 'file, { inner: SymbolIteratorInternal<'data, 'file>, } #[derive(Debug)] enum SymbolIteratorInternal<'data, 'file> where 'data: 'file, { Elf(ElfSymbolIterator<'data, 'file>), MachO(MachOSymbolIterator<'data>), Pe(PeSymbolIterator<'data, 'file>), #[cfg(feature = "wasm")] Wasm(WasmSymbolIterator<'file>), } /// The index used to identify a symbol of a file. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct SymbolIndex(pub usize); /// A symbol table entry. #[derive(Debug)] pub struct Symbol<'data> { kind: SymbolKind, section_index: Option, undefined: bool, global: bool, name: Option<&'data str>, address: u64, size: u64, } /// The kind of a symbol. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SymbolKind { /// The symbol kind is unknown. Unknown, /// The symbol is a null placeholder. Null, /// The symbol is for executable code. Text, /// The symbol is for a data object. Data, /// The symbol is for a section. Section, /// The symbol is the name of a file. It precedes symbols within that file. File, /// The symbol is for an uninitialized common block. Common, /// The symbol is for a thread local storage entity. Tls, } /// A map from addresses to symbols. #[derive(Debug)] pub struct SymbolMap<'data> { symbols: Vec>, } /// A relocation entry. #[derive(Debug)] pub struct Relocation { kind: RelocationKind, size: u8, symbol: SymbolIndex, addend: i64, implicit_addend: bool, } /// The kind of a relocation. /// /// The relocation descriptions use the following definitions. Note that /// these definitions probably don't match any ELF ABI. /// /// * A - The value of the addend. /// * G - The address of the symbol's entry within the global offset table. /// * GOT - The address of the global offset table. /// * L - The address of the symbol's entry within the procedure linkage table. /// * P - The address of the place of the relocation. /// * S - The address of the symbol. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum RelocationKind { /// S + A Absolute, /// S + A AbsoluteSigned, /// S + A - P Relative, /// G + A - GOT GotOffset, /// G + A - P GotRelative, /// L + A - P PltRelative, /// Some other kind of relocation. The value is dependent on file format and machine. Other(u32), } /// An iterator over relocation entries #[derive(Debug)] pub struct RelocationIterator<'data, 'file> where 'data: 'file, { inner: RelocationIteratorInternal<'data, 'file>, } #[derive(Debug)] enum RelocationIteratorInternal<'data, 'file> where 'data: 'file, { Elf(ElfRelocationIterator<'data, 'file>), MachO(MachORelocationIterator<'data, 'file>), Pe(PeRelocationIterator), #[cfg(feature = "wasm")] Wasm(WasmRelocationIterator), } /// Evaluate an expression on the contents of a file format enum. /// /// This is a hack to avoid virtual calls. macro_rules! with_inner { ($inner:expr, $enum:ident, | $var:ident | $body:expr) => { match $inner { $enum::Elf(ref $var) => $body, $enum::MachO(ref $var) => $body, $enum::Pe(ref $var) => $body, #[cfg(feature = "wasm")] $enum::Wasm(ref $var) => $body, } }; } macro_rules! with_inner_mut { ($inner:expr, $enum:ident, | $var:ident | $body:expr) => { match $inner { $enum::Elf(ref mut $var) => $body, $enum::MachO(ref mut $var) => $body, $enum::Pe(ref mut $var) => $body, #[cfg(feature = "wasm")] $enum::Wasm(ref mut $var) => $body, } }; } /// Like `with_inner!`, but wraps the result in another enum. macro_rules! map_inner { ($inner:expr, $from:ident, $to:ident, | $var:ident | $body:expr) => { match $inner { $from::Elf(ref $var) => $to::Elf($body), $from::MachO(ref $var) => $to::MachO($body), $from::Pe(ref $var) => $to::Pe($body), #[cfg(feature = "wasm")] $from::Wasm(ref $var) => $to::Wasm($body), } }; } /// Like `map_inner!`, but the result is a Result or Option. macro_rules! map_inner_option { ($inner:expr, $from:ident, $to:ident, | $var:ident | $body:expr) => { match $inner { $from::Elf(ref $var) => $body.map($to::Elf), $from::MachO(ref $var) => $body.map($to::MachO), $from::Pe(ref $var) => $body.map($to::Pe), #[cfg(feature = "wasm")] $from::Wasm(ref $var) => $body.map($to::Wasm), } }; } /// Call `next` for a file format iterator. macro_rules! next_inner { ($inner:expr, $from:ident, $to:ident) => { match $inner { $from::Elf(ref mut iter) => iter.next().map($to::Elf), $from::MachO(ref mut iter) => iter.next().map($to::MachO), $from::Pe(ref mut iter) => iter.next().map($to::Pe), #[cfg(feature = "wasm")] $from::Wasm(ref mut iter) => iter.next().map($to::Wasm), } }; } #[cfg(feature = "wasm")] fn parse_wasm(data: &[u8]) -> Result>, &'static str> { const WASM_MAGIC: &[u8] = &[0x00, 0x61, 0x73, 0x6D]; if &data[..4] == WASM_MAGIC { let inner = FileInternal::Wasm(WasmFile::parse(data)?); return Ok(Some(File { inner })); } Ok(None) } #[cfg(not(feature = "wasm"))] fn parse_wasm(_data: &[u8]) -> Result, &'static str> { Ok(None) } impl<'data> File<'data> { /// Parse the raw file data. pub fn parse(data: &'data [u8]) -> Result { if data.len() < 16 { return Err("File too short"); } if let Some(wasm) = parse_wasm(data)? { return Ok(wasm); } let mut bytes = [0u8; 16]; bytes.clone_from_slice(&data[..16]); let inner = match goblin::peek_bytes(&bytes).map_err(|_| "Could not parse file magic")? { goblin::Hint::Elf(_) => FileInternal::Elf(ElfFile::parse(data)?), goblin::Hint::Mach(_) => FileInternal::MachO(MachOFile::parse(data)?), goblin::Hint::PE => FileInternal::Pe(PeFile::parse(data)?), _ => return Err("Unknown file magic"), }; Ok(File { inner }) } /// Return the file format. pub fn format(&self) -> Format { match self.inner { FileInternal::Elf(ref inner) => { if inner.is_64() { Format::Elf64 } else { Format::Elf32 } } FileInternal::MachO(ref inner) => { if inner.is_64() { Format::MachO64 } else { Format::MachO32 } } FileInternal::Pe(ref inner) => { if inner.is_64() { Format::Pe64 } else { Format::Pe32 } } #[cfg(feature = "wasm")] FileInternal::Wasm(_) => Format::Wasm, } } } impl<'data, 'file> Object<'data, 'file> for File<'data> where 'data: 'file, { type Segment = Segment<'data, 'file>; type SegmentIterator = SegmentIterator<'data, 'file>; type Section = Section<'data, 'file>; type SectionIterator = SectionIterator<'data, 'file>; type SymbolIterator = SymbolIterator<'data, 'file>; fn machine(&self) -> Machine { with_inner!(self.inner, FileInternal, |x| x.machine()) } fn segments(&'file self) -> SegmentIterator<'data, 'file> { SegmentIterator { inner: map_inner!(self.inner, FileInternal, SegmentIteratorInternal, |x| x .segments()), } } fn section_by_name(&'file self, section_name: &str) -> Option> { map_inner_option!(self.inner, FileInternal, SectionInternal, |x| x .section_by_name(section_name)) .map(|inner| Section { inner }) } fn section_by_index(&'file self, index: SectionIndex) -> Option> { map_inner_option!(self.inner, FileInternal, SectionInternal, |x| x .section_by_index(index)) .map(|inner| Section { inner }) } fn section_data_by_name(&self, section_name: &str) -> Option> { with_inner!(self.inner, FileInternal, |x| x .section_data_by_name(section_name)) } fn sections(&'file self) -> SectionIterator<'data, 'file> { SectionIterator { inner: map_inner!(self.inner, FileInternal, SectionIteratorInternal, |x| x .sections()), } } fn symbol_by_index(&self, index: SymbolIndex) -> Option> { with_inner!(self.inner, FileInternal, |x| x.symbol_by_index(index)) } fn symbols(&'file self) -> SymbolIterator<'data, 'file> { SymbolIterator { inner: map_inner!(self.inner, FileInternal, SymbolIteratorInternal, |x| x .symbols()), } } fn dynamic_symbols(&'file self) -> SymbolIterator<'data, 'file> { SymbolIterator { inner: map_inner!(self.inner, FileInternal, SymbolIteratorInternal, |x| x .dynamic_symbols()), } } fn symbol_map(&self) -> SymbolMap<'data> { with_inner!(self.inner, FileInternal, |x| x.symbol_map()) } fn is_little_endian(&self) -> bool { with_inner!(self.inner, FileInternal, |x| x.is_little_endian()) } fn has_debug_symbols(&self) -> bool { with_inner!(self.inner, FileInternal, |x| x.has_debug_symbols()) } #[inline] fn mach_uuid(&self) -> Option { with_inner!(self.inner, FileInternal, |x| x.mach_uuid()) } #[inline] fn build_id(&self) -> Option<&'data [u8]> { with_inner!(self.inner, FileInternal, |x| x.build_id()) } #[inline] fn gnu_debuglink(&self) -> Option<(&'data [u8], u32)> { with_inner!(self.inner, FileInternal, |x| x.gnu_debuglink()) } fn entry(&self) -> u64 { with_inner!(self.inner, FileInternal, |x| x.entry()) } } impl<'data, 'file> Iterator for SegmentIterator<'data, 'file> { type Item = Segment<'data, 'file>; fn next(&mut self) -> Option { next_inner!(self.inner, SegmentIteratorInternal, SegmentInternal) .map(|inner| Segment { inner }) } } impl<'data, 'file> fmt::Debug for Segment<'data, 'file> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // It's painful to do much better than this f.debug_struct("Segment") .field("name", &self.name().unwrap_or("")) .field("address", &self.address()) .field("size", &self.data().len()) .finish() } } impl<'data, 'file> ObjectSegment<'data> for Segment<'data, 'file> { fn address(&self) -> u64 { with_inner!(self.inner, SegmentInternal, |x| x.address()) } fn size(&self) -> u64 { with_inner!(self.inner, SegmentInternal, |x| x.size()) } fn align(&self) -> u64 { with_inner!(self.inner, SegmentInternal, |x| x.align()) } fn data(&self) -> &'data [u8] { with_inner!(self.inner, SegmentInternal, |x| x.data()) } fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]> { with_inner!(self.inner, SegmentInternal, |x| x.data_range(address, size)) } fn name(&self) -> Option<&str> { with_inner!(self.inner, SegmentInternal, |x| x.name()) } } impl<'data, 'file> Iterator for SectionIterator<'data, 'file> { type Item = Section<'data, 'file>; fn next(&mut self) -> Option { next_inner!(self.inner, SectionIteratorInternal, SectionInternal) .map(|inner| Section { inner }) } } impl<'data, 'file> fmt::Debug for Section<'data, 'file> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // It's painful to do much better than this f.debug_struct("Section") .field("name", &self.name().unwrap_or("")) .field("address", &self.address()) .field("size", &self.data().len()) .field("kind", &self.kind()) .finish() } } impl<'data, 'file> ObjectSection<'data> for Section<'data, 'file> { type RelocationIterator = RelocationIterator<'data, 'file>; fn index(&self) -> SectionIndex { with_inner!(self.inner, SectionInternal, |x| x.index()) } fn address(&self) -> u64 { with_inner!(self.inner, SectionInternal, |x| x.address()) } fn size(&self) -> u64 { with_inner!(self.inner, SectionInternal, |x| x.size()) } fn align(&self) -> u64 { with_inner!(self.inner, SectionInternal, |x| x.align()) } fn data(&self) -> Cow<'data, [u8]> { with_inner!(self.inner, SectionInternal, |x| x.data()) } fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]> { with_inner!(self.inner, SectionInternal, |x| x.data_range(address, size)) } fn uncompressed_data(&self) -> Cow<'data, [u8]> { with_inner!(self.inner, SectionInternal, |x| x.uncompressed_data()) } fn name(&self) -> Option<&str> { with_inner!(self.inner, SectionInternal, |x| x.name()) } fn segment_name(&self) -> Option<&str> { with_inner!(self.inner, SectionInternal, |x| x.segment_name()) } fn kind(&self) -> SectionKind { with_inner!(self.inner, SectionInternal, |x| x.kind()) } fn relocations(&self) -> RelocationIterator<'data, 'file> { RelocationIterator { inner: map_inner!( self.inner, SectionInternal, RelocationIteratorInternal, |x| x.relocations() ), } } } impl<'data, 'file> Iterator for SymbolIterator<'data, 'file> { type Item = (SymbolIndex, Symbol<'data>); fn next(&mut self) -> Option { with_inner_mut!(self.inner, SymbolIteratorInternal, |x| x.next()) } } impl<'data> Symbol<'data> { /// Return the kind of this symbol. #[inline] pub fn kind(&self) -> SymbolKind { self.kind } /// Returns the section index for the section containing this symbol. /// /// May return `None` if the section is unknown or the symbol is undefined. #[inline] pub fn section_index(&self) -> Option { self.section_index } /// Return true if the symbol is undefined. #[inline] pub fn is_undefined(&self) -> bool { self.undefined } /// Return true if the symbol is global. #[inline] pub fn is_global(&self) -> bool { self.global } /// Return true if the symbol is local. #[inline] pub fn is_local(&self) -> bool { !self.is_global() } /// The name of the symbol. #[inline] pub fn name(&self) -> Option<&'data str> { self.name } /// The address of the symbol. May be zero if the address is unknown. #[inline] pub fn address(&self) -> u64 { self.address } /// The size of the symbol. May be zero if the size is unknown. #[inline] pub fn size(&self) -> u64 { self.size } } impl<'data> SymbolMap<'data> { /// Get the symbol containing the given address. pub fn get(&self, address: u64) -> Option<&Symbol<'data>> { self.symbols .binary_search_by(|symbol| { if address < symbol.address { std::cmp::Ordering::Greater } else if address < symbol.address + symbol.size { std::cmp::Ordering::Equal } else { std::cmp::Ordering::Less } }) .ok() .and_then(|index| self.symbols.get(index)) } /// Get all symbols in the map. pub fn symbols(&self) -> &[Symbol<'data>] { &self.symbols } /// Return true for symbols that should be included in the map. fn filter(symbol: &Symbol<'_>) -> bool { match symbol.kind() { SymbolKind::Unknown | SymbolKind::Text | SymbolKind::Data => {} SymbolKind::Null | SymbolKind::Section | SymbolKind::File | SymbolKind::Common | SymbolKind::Tls => { return false; } } !symbol.is_undefined() && symbol.size() > 0 } } impl<'data, 'file> Iterator for RelocationIterator<'data, 'file> { type Item = (u64, Relocation); fn next(&mut self) -> Option { with_inner_mut!(self.inner, RelocationIteratorInternal, |x| x.next()) } } impl Relocation { /// The kind of relocation. #[inline] pub fn kind(&self) -> RelocationKind { self.kind } /// The size in bits of the place of the relocation. /// /// If 0, then the size is determined by the relocation kind. #[inline] pub fn size(&self) -> u8 { self.size } /// The index of the symbol within the symbol table, if applicable. #[inline] pub fn symbol(&self) -> SymbolIndex { self.symbol } /// The addend to use in the relocation calculation. pub fn addend(&self) -> i64 { self.addend } /// Set the addend to use in the relocation calculation. pub fn set_addend(&mut self, addend: i64) { self.addend = addend } /// Returns true if there is an implicit addend stored in the data at the offset /// to be relocated. pub fn has_implicit_addend(&self) -> bool { self.implicit_addend } } fn data_range(data: &[u8], data_address: u64, range_address: u64, size: u64) -> Option<&[u8]> { if range_address >= data_address { let start_offset = (range_address - data_address) as usize; let end_offset = start_offset + size as usize; if end_offset <= data.len() { return Some(&data[start_offset..end_offset]); } } None } object-0.12.0/src/macho.rs010066400017500001750000000360601346047733500135300ustar0000000000000000use crate::alloc::borrow::Cow; use crate::alloc::vec::Vec; use std::{fmt, iter, slice}; use goblin::container; use goblin::mach; use goblin::mach::load_command::CommandVariant; use uuid::Uuid; use crate::{ Machine, Object, ObjectSection, ObjectSegment, Relocation, RelocationKind, SectionIndex, SectionKind, Symbol, SymbolIndex, SymbolKind, SymbolMap, }; /// A Mach-O object file. #[derive(Debug)] pub struct MachOFile<'data> { macho: mach::MachO<'data>, data: &'data [u8], ctx: container::Ctx, } /// An iterator over the segments of a `MachOFile`. #[derive(Debug)] pub struct MachOSegmentIterator<'data, 'file> where 'data: 'file, { segments: slice::Iter<'file, mach::segment::Segment<'data>>, } /// A segment of a `MachOFile`. #[derive(Debug)] pub struct MachOSegment<'data, 'file> where 'data: 'file, { segment: &'file mach::segment::Segment<'data>, } /// An iterator over the sections of a `MachOFile`. pub struct MachOSectionIterator<'data, 'file> where 'data: 'file, { file: &'file MachOFile<'data>, index: usize, segments: slice::Iter<'file, mach::segment::Segment<'data>>, sections: Option>, } /// A section of a `MachOFile`. #[derive(Debug)] pub struct MachOSection<'data, 'file> where 'data: 'file, { file: &'file MachOFile<'data>, // index is 1-based (equivalent to n_sect field in symbols) index: SectionIndex, section: mach::segment::Section, data: mach::segment::SectionData<'data>, } /// An iterator over the symbols of a `MachOFile`. pub struct MachOSymbolIterator<'data> { symbols: iter::Enumerate>, } /// An iterator over the relocations in an `MachOSection`. pub struct MachORelocationIterator<'data, 'file> where 'data: 'file, { file: &'file MachOFile<'data>, relocations: mach::segment::RelocationIterator<'data>, } impl<'data> MachOFile<'data> { /// Get the Mach-O headers of the file. // TODO: this is temporary to allow access to features this crate doesn't provide yet #[inline] pub fn macho(&self) -> &mach::MachO<'data> { &self.macho } /// Parse the raw Mach-O file data. pub fn parse(data: &'data [u8]) -> Result { let (_magic, ctx) = mach::parse_magic_and_ctx(data, 0).map_err(|_| "Could not parse Mach-O magic")?; let ctx = ctx.ok_or("Invalid Mach-O magic")?; let macho = mach::MachO::parse(data, 0).map_err(|_| "Could not parse Mach-O header")?; Ok(MachOFile { macho, data, ctx }) } /// True for 64-bit files. #[inline] pub fn is_64(&self) -> bool { self.macho.is_64 } } impl<'data, 'file> Object<'data, 'file> for MachOFile<'data> where 'data: 'file, { type Segment = MachOSegment<'data, 'file>; type SegmentIterator = MachOSegmentIterator<'data, 'file>; type Section = MachOSection<'data, 'file>; type SectionIterator = MachOSectionIterator<'data, 'file>; type SymbolIterator = MachOSymbolIterator<'data>; fn machine(&self) -> Machine { match self.macho.header.cputype { mach::cputype::CPU_TYPE_ARM => Machine::Arm, mach::cputype::CPU_TYPE_ARM64 => Machine::Arm64, mach::cputype::CPU_TYPE_X86 => Machine::X86, mach::cputype::CPU_TYPE_X86_64 => Machine::X86_64, mach::cputype::CPU_TYPE_MIPS => Machine::Mips, _ => Machine::Other, } } fn segments(&'file self) -> MachOSegmentIterator<'data, 'file> { MachOSegmentIterator { segments: self.macho.segments.iter(), } } fn section_by_name(&'file self, section_name: &str) -> Option> { // Translate the "." prefix to the "__" prefix used by OSX/Mach-O, eg // ".debug_info" to "__debug_info". let (system_section, section_name) = if section_name.starts_with('.') { (true, §ion_name[1..]) } else { (false, section_name) }; let cmp_section_name = |section: &MachOSection| { section .name() .map(|name| { if system_section { name.starts_with("__") && section_name == &name[2..] } else { section_name == name } }) .unwrap_or(false) }; self.sections().find(cmp_section_name) } fn section_by_index(&'file self, index: SectionIndex) -> Option> { self.sections().find(|section| section.index() == index) } fn sections(&'file self) -> MachOSectionIterator<'data, 'file> { MachOSectionIterator { file: self, index: 1, segments: self.macho.segments.iter(), sections: None, } } fn symbol_by_index(&self, index: SymbolIndex) -> Option> { self.macho .symbols .as_ref() .and_then(|symbols| symbols.get(index.0).ok()) .and_then(|(name, nlist)| parse_symbol(name, &nlist)) } fn symbols(&'file self) -> MachOSymbolIterator<'data> { let symbols = match self.macho.symbols { Some(ref symbols) => symbols.into_iter(), None => mach::symbols::SymbolIterator::default(), } .enumerate(); MachOSymbolIterator { symbols } } fn dynamic_symbols(&'file self) -> MachOSymbolIterator<'data> { // The LC_DYSYMTAB command contains indices into the same symbol // table as the LC_SYMTAB command, so return all of them. self.symbols() } fn symbol_map(&self) -> SymbolMap<'data> { let mut symbols: Vec<_> = self.symbols().map(|(_, s)| s).collect(); // Add symbols for the end of each section. for section in self.sections() { symbols.push(Symbol { name: None, address: section.address() + section.size(), size: 0, kind: SymbolKind::Section, section_index: None, undefined: false, global: false, }); } // Calculate symbol sizes by sorting and finding the next symbol. symbols.sort_by(|a, b| { a.address.cmp(&b.address).then_with(|| { // Place the end of section symbols last. (a.kind == SymbolKind::Section).cmp(&(b.kind == SymbolKind::Section)) }) }); for i in 0..symbols.len() { let (before, after) = symbols.split_at_mut(i + 1); let symbol = &mut before[i]; if symbol.kind != SymbolKind::Section { if let Some(next) = after .iter() .skip_while(|x| x.kind != SymbolKind::Section && x.address == symbol.address) .next() { symbol.size = next.address - symbol.address; } } } symbols.retain(SymbolMap::filter); SymbolMap { symbols } } #[inline] fn is_little_endian(&self) -> bool { self.macho.little_endian } fn has_debug_symbols(&self) -> bool { self.section_data_by_name(".debug_info").is_some() } fn mach_uuid(&self) -> Option { // Return the UUID from the `LC_UUID` load command, if one is present. self.macho .load_commands .iter() .filter_map(|lc| { match lc.command { CommandVariant::Uuid(ref cmd) => { //TODO: Uuid should have a `from_array` method that can't fail. Uuid::from_slice(&cmd.uuid).ok() } _ => None, } }) .nth(0) } fn entry(&self) -> u64 { self.macho.entry } } impl<'data, 'file> Iterator for MachOSegmentIterator<'data, 'file> { type Item = MachOSegment<'data, 'file>; fn next(&mut self) -> Option { self.segments.next().map(|segment| MachOSegment { segment }) } } impl<'data, 'file> ObjectSegment<'data> for MachOSegment<'data, 'file> { #[inline] fn address(&self) -> u64 { self.segment.vmaddr } #[inline] fn size(&self) -> u64 { self.segment.vmsize } #[inline] fn align(&self) -> u64 { // Page size. 0x1000 } #[inline] fn data(&self) -> &'data [u8] { self.segment.data } fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]> { crate::data_range(self.data(), self.address(), address, size) } #[inline] fn name(&self) -> Option<&str> { self.segment.name().ok() } } impl<'data, 'file> fmt::Debug for MachOSectionIterator<'data, 'file> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // It's painful to do much better than this f.debug_struct("MachOSectionIterator").finish() } } impl<'data, 'file> Iterator for MachOSectionIterator<'data, 'file> { type Item = MachOSection<'data, 'file>; fn next(&mut self) -> Option { loop { if let Some(ref mut sections) = self.sections { if let Some(section_result) = sections.next() { if let Ok((section, data)) = section_result { let index = SectionIndex(self.index); self.index += 1; return Some(MachOSection { file: self.file, index, section, data, }); } else { // We have to stop iterating, otherwise section indices // will be wrong. self.sections = None; self.segments = [].iter(); return None; } } } match self.segments.next() { None => return None, Some(segment) => { self.sections = Some(segment.into_iter()); } } } } } impl<'data, 'file> ObjectSection<'data> for MachOSection<'data, 'file> { type RelocationIterator = MachORelocationIterator<'data, 'file>; #[inline] fn index(&self) -> SectionIndex { self.index } #[inline] fn address(&self) -> u64 { self.section.addr } #[inline] fn size(&self) -> u64 { self.section.size } #[inline] fn align(&self) -> u64 { 1 << self.section.align } #[inline] fn data(&self) -> Cow<'data, [u8]> { Cow::from(self.data) } fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]> { crate::data_range(self.data, self.address(), address, size) } #[inline] fn uncompressed_data(&self) -> Cow<'data, [u8]> { // TODO: does MachO support compression? self.data() } #[inline] fn name(&self) -> Option<&str> { self.section.name().ok() } #[inline] fn segment_name(&self) -> Option<&str> { self.section.segname().ok() } fn kind(&self) -> SectionKind { match (self.segment_name(), self.name()) { (Some("__TEXT"), Some("__text")) => SectionKind::Text, (Some("__DATA"), Some("__data")) => SectionKind::Data, (Some("__DATA"), Some("__bss")) => SectionKind::UninitializedData, _ => SectionKind::Other, } } fn relocations(&self) -> MachORelocationIterator<'data, 'file> { MachORelocationIterator { file: self.file, relocations: self.section.iter_relocations(self.file.data, self.file.ctx), } } } impl<'data> fmt::Debug for MachOSymbolIterator<'data> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("MachOSymbolIterator").finish() } } impl<'data> Iterator for MachOSymbolIterator<'data> { type Item = (SymbolIndex, Symbol<'data>); fn next(&mut self) -> Option { while let Some((index, Ok((name, nlist)))) = self.symbols.next() { if let Some(symbol) = parse_symbol(name, &nlist) { return Some((SymbolIndex(index), symbol)); } } None } } fn parse_symbol<'data>(name: &'data str, nlist: &mach::symbols::Nlist) -> Option> { if nlist.n_type & mach::symbols::N_STAB != 0 { return None; } let n_type = nlist.n_type & mach::symbols::NLIST_TYPE_MASK; let section_index = if n_type == mach::symbols::N_SECT { if nlist.n_sect == 0 { None } else { Some(SectionIndex(nlist.n_sect)) } } else { // TODO: better handling for other n_type values None }; Some(Symbol { name: Some(name), address: nlist.n_value, // Only calculated for symbol maps size: 0, kind: SymbolKind::Unknown, section_index, undefined: nlist.is_undefined(), global: nlist.is_global(), }) } impl<'data, 'file> Iterator for MachORelocationIterator<'data, 'file> { type Item = (u64, Relocation); fn next(&mut self) -> Option { self.relocations.next()?.ok().map(|reloc| { let kind = match self.file.macho.header.cputype { mach::cputype::CPU_TYPE_ARM => match reloc.r_type() { mach::relocation::ARM_RELOC_VANILLA => RelocationKind::Absolute, _ => RelocationKind::Other(reloc.r_info), }, mach::cputype::CPU_TYPE_ARM64 => match reloc.r_type() { mach::relocation::ARM64_RELOC_UNSIGNED => RelocationKind::Absolute, _ => RelocationKind::Other(reloc.r_info), }, mach::cputype::CPU_TYPE_X86 => match reloc.r_type() { mach::relocation::GENERIC_RELOC_VANILLA => RelocationKind::Absolute, _ => RelocationKind::Other(reloc.r_info), }, mach::cputype::CPU_TYPE_X86_64 => match reloc.r_type() { mach::relocation::X86_64_RELOC_UNSIGNED => RelocationKind::Absolute, _ => RelocationKind::Other(reloc.r_info), }, _ => RelocationKind::Other(reloc.r_info), }; let size = reloc.r_length() * 8; // FIXME: reloc.r_pcrel() ( reloc.r_address as u64, Relocation { kind, size, // FIXME: handle reloc.is_extern() symbol: SymbolIndex(reloc.r_symbolnum()), addend: 0, implicit_addend: true, }, ) }) } } impl<'data, 'file> fmt::Debug for MachORelocationIterator<'data, 'file> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("MachORelocationIterator").finish() } } object-0.12.0/src/pe.rs010066400017500001750000000242601346047733500130440ustar0000000000000000use crate::alloc::borrow::Cow; use crate::alloc::vec::Vec; use std::{cmp, iter, slice}; use goblin::pe; use crate::{ Machine, Object, ObjectSection, ObjectSegment, Relocation, SectionIndex, SectionKind, Symbol, SymbolIndex, SymbolKind, SymbolMap, }; /// A PE object file. #[derive(Debug)] pub struct PeFile<'data> { pe: pe::PE<'data>, data: &'data [u8], } /// An iterator over the loadable sections of a `PeFile`. #[derive(Debug)] pub struct PeSegmentIterator<'data, 'file> where 'data: 'file, { file: &'file PeFile<'data>, iter: slice::Iter<'file, pe::section_table::SectionTable>, } /// A loadable section of a `PeFile`. #[derive(Debug)] pub struct PeSegment<'data, 'file> where 'data: 'file, { file: &'file PeFile<'data>, section: &'file pe::section_table::SectionTable, } /// An iterator over the sections of a `PeFile`. #[derive(Debug)] pub struct PeSectionIterator<'data, 'file> where 'data: 'file, { file: &'file PeFile<'data>, iter: iter::Enumerate>, } /// A section of a `PeFile`. #[derive(Debug)] pub struct PeSection<'data, 'file> where 'data: 'file, { file: &'file PeFile<'data>, index: SectionIndex, section: &'file pe::section_table::SectionTable, } /// An iterator over the symbols of a `PeFile`. #[derive(Debug)] pub struct PeSymbolIterator<'data, 'file> where 'data: 'file, { index: usize, exports: slice::Iter<'file, pe::export::Export<'data>>, imports: slice::Iter<'file, pe::import::Import<'data>>, } /// An iterator over the relocations in an `PeSection`. #[derive(Debug)] pub struct PeRelocationIterator; impl<'data> PeFile<'data> { /// Get the PE headers of the file. // TODO: this is temporary to allow access to features this crate doesn't provide yet #[inline] pub fn pe(&self) -> &pe::PE<'data> { &self.pe } /// Parse the raw PE file data. pub fn parse(data: &'data [u8]) -> Result { let pe = pe::PE::parse(data).map_err(|_| "Could not parse PE header")?; Ok(PeFile { pe, data }) } /// True for 64-bit files. #[inline] pub fn is_64(&self) -> bool { self.pe.is_64 } fn section_alignment(&self) -> u64 { u64::from( self.pe .header .optional_header .map(|h| h.windows_fields.section_alignment) .unwrap_or(0x1000), ) } } impl<'data, 'file> Object<'data, 'file> for PeFile<'data> where 'data: 'file, { type Segment = PeSegment<'data, 'file>; type SegmentIterator = PeSegmentIterator<'data, 'file>; type Section = PeSection<'data, 'file>; type SectionIterator = PeSectionIterator<'data, 'file>; type SymbolIterator = PeSymbolIterator<'data, 'file>; fn machine(&self) -> Machine { match self.pe.header.coff_header.machine { // TODO: Arm/Arm64 pe::header::COFF_MACHINE_X86 => Machine::X86, pe::header::COFF_MACHINE_X86_64 => Machine::X86_64, _ => Machine::Other, } } fn segments(&'file self) -> PeSegmentIterator<'data, 'file> { PeSegmentIterator { file: self, iter: self.pe.sections.iter(), } } fn section_by_name(&'file self, section_name: &str) -> Option> { self.sections() .find(|section| section.name() == Some(section_name)) } fn section_by_index(&'file self, index: SectionIndex) -> Option> { self.sections().find(|section| section.index() == index) } fn sections(&'file self) -> PeSectionIterator<'data, 'file> { PeSectionIterator { file: self, iter: self.pe.sections.iter().enumerate(), } } fn symbol_by_index(&self, _index: SymbolIndex) -> Option> { // TODO: return COFF symbols for object files None } fn symbols(&'file self) -> PeSymbolIterator<'data, 'file> { // TODO: return COFF symbols for object files PeSymbolIterator { index: 0, exports: [].iter(), imports: [].iter(), } } fn dynamic_symbols(&'file self) -> PeSymbolIterator<'data, 'file> { PeSymbolIterator { index: 0, exports: self.pe.exports.iter(), imports: self.pe.imports.iter(), } } fn symbol_map(&self) -> SymbolMap<'data> { // TODO: untested let mut symbols: Vec<_> = self .symbols() .map(|(_, s)| s) .filter(SymbolMap::filter) .collect(); symbols.sort_by_key(|x| x.address); SymbolMap { symbols } } #[inline] fn is_little_endian(&self) -> bool { // TODO: always little endian? The COFF header has some bits in the // characteristics flags, but these are obsolete. true } fn has_debug_symbols(&self) -> bool { // TODO: check if CodeView-in-PE still works for section in &self.pe.sections { if let Ok(name) = section.name() { if name == ".debug_info" { return true; } } } false } fn entry(&self) -> u64 { self.pe.entry as u64 } } impl<'data, 'file> Iterator for PeSegmentIterator<'data, 'file> { type Item = PeSegment<'data, 'file>; fn next(&mut self) -> Option { self.iter.next().map(|section| PeSegment { file: self.file, section, }) } } impl<'data, 'file> ObjectSegment<'data> for PeSegment<'data, 'file> { #[inline] fn address(&self) -> u64 { u64::from(self.section.virtual_address) } #[inline] fn size(&self) -> u64 { u64::from(self.section.virtual_size) } #[inline] fn align(&self) -> u64 { self.file.section_alignment() } fn data(&self) -> &'data [u8] { let offset = self.section.pointer_to_raw_data as usize; let size = cmp::min(self.section.virtual_size, self.section.size_of_raw_data) as usize; &self.file.data[offset..][..size] } fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]> { crate::data_range(self.data(), self.address(), address, size) } #[inline] fn name(&self) -> Option<&str> { self.section.name().ok() } } impl<'data, 'file> Iterator for PeSectionIterator<'data, 'file> { type Item = PeSection<'data, 'file>; fn next(&mut self) -> Option { self.iter.next().map(|(index, section)| PeSection { file: self.file, index: SectionIndex(index), section, }) } } impl<'data, 'file> PeSection<'data, 'file> { fn raw_data(&self) -> &'data [u8] { let offset = self.section.pointer_to_raw_data as usize; let size = cmp::min(self.section.virtual_size, self.section.size_of_raw_data) as usize; &self.file.data[offset..][..size] } } impl<'data, 'file> ObjectSection<'data> for PeSection<'data, 'file> { type RelocationIterator = PeRelocationIterator; #[inline] fn index(&self) -> SectionIndex { self.index } #[inline] fn address(&self) -> u64 { u64::from(self.section.virtual_address) } #[inline] fn size(&self) -> u64 { u64::from(self.section.virtual_size) } #[inline] fn align(&self) -> u64 { self.file.section_alignment() } fn data(&self) -> Cow<'data, [u8]> { Cow::from(self.raw_data()) } fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]> { crate::data_range(self.raw_data(), self.address(), address, size) } #[inline] fn uncompressed_data(&self) -> Cow<'data, [u8]> { // TODO: does PE support compression? self.data() } fn name(&self) -> Option<&str> { self.section.name().ok() } #[inline] fn segment_name(&self) -> Option<&str> { None } #[inline] fn kind(&self) -> SectionKind { if self.section.characteristics & (pe::section_table::IMAGE_SCN_CNT_CODE | pe::section_table::IMAGE_SCN_MEM_EXECUTE) != 0 { SectionKind::Text } else if self.section.characteristics & pe::section_table::IMAGE_SCN_CNT_INITIALIZED_DATA != 0 { SectionKind::Data } else if self.section.characteristics & pe::section_table::IMAGE_SCN_CNT_UNINITIALIZED_DATA != 0 { SectionKind::UninitializedData } else { SectionKind::Unknown } } fn relocations(&self) -> PeRelocationIterator { PeRelocationIterator } } impl<'data, 'file> Iterator for PeSymbolIterator<'data, 'file> { type Item = (SymbolIndex, Symbol<'data>); fn next(&mut self) -> Option { if let Some(export) = self.exports.next() { let index = SymbolIndex(self.index); self.index += 1; return Some(( index, Symbol { kind: SymbolKind::Unknown, // TODO: can we find a section? section_index: None, undefined: false, global: true, name: export.name, address: export.rva as u64, size: 0, }, )); } if let Some(import) = self.imports.next() { let index = SymbolIndex(self.index); self.index += 1; let name = match import.name { Cow::Borrowed(name) => Some(name), _ => None, }; return Some(( index, Symbol { kind: SymbolKind::Unknown, section_index: None, undefined: true, global: true, name, address: 0, size: 0, }, )); } None } } impl Iterator for PeRelocationIterator { type Item = (u64, Relocation); fn next(&mut self) -> Option { None } } object-0.12.0/src/traits.rs010066400017500001750000000157361346047733500137560ustar0000000000000000use crate::alloc::borrow::Cow; use crate::{Machine, Relocation, SectionIndex, SectionKind, Symbol, SymbolIndex, SymbolMap, Uuid}; /// An object file. pub trait Object<'data, 'file> { /// A segment in the object file. type Segment: ObjectSegment<'data>; /// An iterator over the segments in the object file. type SegmentIterator: Iterator; /// A section in the object file. type Section: ObjectSection<'data>; /// An iterator over the sections in the object file. type SectionIterator: Iterator; /// An iterator over the symbols in the object file. type SymbolIterator: Iterator)>; /// Get the machine type of the file. fn machine(&self) -> Machine; /// Get an iterator over the segments in the file. fn segments(&'file self) -> Self::SegmentIterator; /// Get the entry point address of the binary fn entry(&'file self) -> u64; /// Get the section named `section_name`, if such a section exists. /// /// If `section_name` starts with a '.' then it is treated as a system section name, /// and is compared using the conventions specific to the object file format. This /// includes: /// - if ".text" is requested for a Mach-O object file, then the actual /// section name that is searched for is "__text". /// - if ".debug_info" is requested for an ELF object file, then /// ".zdebug_info" may be returned (and similarly for other debug sections). /// /// For some object files, multiple segments may contain sections with the same /// name. In this case, the first matching section will be used. fn section_by_name(&'file self, section_name: &str) -> Option; /// Get the section at the given index. /// /// The meaning of the index depends on the object file. /// /// For some object files, this requires iterating through all sections. fn section_by_index(&'file self, index: SectionIndex) -> Option; /// Get the contents of the section named `section_name`, if such /// a section exists. /// /// The `section_name` is interpreted according to `Self::section_by_name`. /// /// This may decompress section data. fn section_data_by_name(&'file self, section_name: &str) -> Option> { self.section_by_name(section_name) .map(|section| section.uncompressed_data()) } /// Get an iterator over the sections in the file. fn sections(&'file self) -> Self::SectionIterator; /// Get the debugging symbol at the given index. /// /// This is similar to `self.symbols().nth(index)`, except that /// the index will take into account malformed or unsupported symbols. fn symbol_by_index(&self, index: SymbolIndex) -> Option>; /// Get an iterator over the debugging symbols in the file. /// /// This may skip over symbols that are malformed or unsupported. fn symbols(&'file self) -> Self::SymbolIterator; /// Get the data for the given symbol. fn symbol_data(&'file self, symbol: &Symbol<'data>) -> Option<&'data [u8]> { if symbol.is_undefined() { return None; } let address = symbol.address(); let size = symbol.size(); if let Some(index) = symbol.section_index() { self.section_by_index(index) .and_then(|section| section.data_range(address, size)) } else { self.segments() .find_map(|segment| segment.data_range(address, size)) } } /// Get an iterator over the dynamic linking symbols in the file. /// /// This may skip over symbols that are malformed or unsupported. fn dynamic_symbols(&'file self) -> Self::SymbolIterator; /// Construct a map from addresses to symbols. fn symbol_map(&self) -> SymbolMap<'data>; /// Return true if the file is little endian, false if it is big endian. fn is_little_endian(&self) -> bool; /// Return true if the file contains debug information sections, false if not. fn has_debug_symbols(&self) -> bool; /// The UUID from a Mach-O `LC_UUID` load command. #[inline] fn mach_uuid(&self) -> Option { None } /// The build ID from an ELF `NT_GNU_BUILD_ID` note. #[inline] fn build_id(&self) -> Option<&'data [u8]> { None } /// The filename and CRC from a `.gnu_debuglink` section. #[inline] fn gnu_debuglink(&self) -> Option<(&'data [u8], u32)> { None } } /// A loadable segment defined in an object file. /// /// For ELF, this is a program header with type `PT_LOAD`. /// For Mach-O, this is a load command with type `LC_SEGMENT` or `LC_SEGMENT_64`. pub trait ObjectSegment<'data> { /// Returns the virtual address of the segment. fn address(&self) -> u64; /// Returns the size of the segment in memory. fn size(&self) -> u64; /// Returns the alignment of the segment in memory. fn align(&self) -> u64; /// Returns a reference to the file contents of the segment. /// The length of this data may be different from the size of the /// segment in memory. fn data(&self) -> &'data [u8]; /// Return the segment data in the given range. fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]>; /// Returns the name of the segment. fn name(&self) -> Option<&str>; } /// A section defined in an object file. pub trait ObjectSection<'data> { /// An iterator over the relocations for a section. /// /// The first field in the item tuple is the section offset /// that the relocation applies to. type RelocationIterator: Iterator; /// Returns the section index. fn index(&self) -> SectionIndex; /// Returns the address of the section. fn address(&self) -> u64; /// Returns the size of the section in memory. fn size(&self) -> u64; /// Returns the alignment of the section in memory. fn align(&self) -> u64; /// Returns the raw contents of the section. /// The length of this data may be different from the size of the /// section in memory. /// /// This does not do any decompression. fn data(&self) -> Cow<'data, [u8]>; /// Return the raw contents of the section data in the given range. /// /// This does not do any decompression. fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]>; /// Returns the uncompressed contents of the section. /// The length of this data may be different from the size of the /// section in memory. fn uncompressed_data(&self) -> Cow<'data, [u8]>; /// Returns the name of the section. fn name(&self) -> Option<&str>; /// Returns the name of the segment for this section. fn segment_name(&self) -> Option<&str>; /// Return the kind of this section. fn kind(&self) -> SectionKind; /// Get the relocations for this section. fn relocations(&self) -> Self::RelocationIterator; } object-0.12.0/src/wasm.rs010066400017500001750000000202541346047733500134060ustar0000000000000000use crate::alloc::vec::Vec; use parity_wasm::elements::{self, Deserialize}; use std::borrow::Cow; use std::{iter, slice}; use crate::{ Machine, Object, ObjectSection, ObjectSegment, Relocation, SectionIndex, SectionKind, Symbol, SymbolIndex, SymbolMap, }; /// A WebAssembly object file. #[derive(Debug)] pub struct WasmFile { module: elements::Module, } impl<'data> WasmFile { /// Parse the raw wasm data. pub fn parse(mut data: &'data [u8]) -> Result { let module = elements::Module::deserialize(&mut data).map_err(|_| "failed to parse wasm")?; Ok(WasmFile { module }) } } /// An iterator over the segments of an `WasmFile`. #[derive(Debug)] pub struct WasmSegmentIterator<'file> { file: &'file WasmFile, } /// A segment of an `WasmFile`. #[derive(Debug)] pub struct WasmSegment<'file> { file: &'file WasmFile, } /// An iterator over the sections of an `WasmFile`. #[derive(Debug)] pub struct WasmSectionIterator<'file> { sections: iter::Enumerate>, } /// A section of an `WasmFile`. #[derive(Debug)] pub struct WasmSection<'file> { index: SectionIndex, section: &'file elements::Section, } /// An iterator over the symbols of an `WasmFile`. #[derive(Debug)] pub struct WasmSymbolIterator<'file> { file: &'file WasmFile, } /// An iterator over the relocations in an `WasmSection`. #[derive(Debug)] pub struct WasmRelocationIterator; fn serialize_to_cow<'a, S>(s: S) -> Option> where S: elements::Serialize, { let mut buf = Vec::new(); s.serialize(&mut buf).ok()?; Some(Cow::from(buf)) } impl<'file> Object<'static, 'file> for WasmFile { type Segment = WasmSegment<'file>; type SegmentIterator = WasmSegmentIterator<'file>; type Section = WasmSection<'file>; type SectionIterator = WasmSectionIterator<'file>; type SymbolIterator = WasmSymbolIterator<'file>; fn machine(&self) -> Machine { Machine::Other } fn segments(&'file self) -> Self::SegmentIterator { WasmSegmentIterator { file: self } } fn entry(&'file self) -> u64 { self.module .start_section() .map_or(u64::max_value(), u64::from) } fn section_by_name(&'file self, section_name: &str) -> Option> { self.sections() .find(|section| section.name() == Some(section_name)) } fn section_by_index(&'file self, index: SectionIndex) -> Option> { self.sections().find(|section| section.index() == index) } fn sections(&'file self) -> Self::SectionIterator { WasmSectionIterator { sections: self.module.sections().iter().enumerate(), } } fn symbol_by_index(&self, _index: SymbolIndex) -> Option> { unimplemented!() } fn symbols(&'file self) -> Self::SymbolIterator { WasmSymbolIterator { file: self } } fn dynamic_symbols(&'file self) -> Self::SymbolIterator { WasmSymbolIterator { file: self } } fn symbol_map(&self) -> SymbolMap<'static> { SymbolMap { symbols: Vec::new(), } } fn is_little_endian(&self) -> bool { true } fn has_debug_symbols(&self) -> bool { // We ignore the "name" section, and use this to mean whether the wasm // has DWARF. self.module.sections().iter().any(|s| match *s { elements::Section::Custom(ref c) => c.name().starts_with(".debug_"), _ => false, }) } } impl<'file> Iterator for WasmSegmentIterator<'file> { type Item = WasmSegment<'file>; fn next(&mut self) -> Option { None } } impl<'file> ObjectSegment<'static> for WasmSegment<'file> { #[inline] fn address(&self) -> u64 { unreachable!() } #[inline] fn size(&self) -> u64 { unreachable!() } #[inline] fn align(&self) -> u64 { unreachable!() } fn data(&self) -> &'static [u8] { unreachable!() } fn data_range(&self, _address: u64, _size: u64) -> Option<&'static [u8]> { unreachable!() } #[inline] fn name(&self) -> Option<&str> { unreachable!() } } impl<'file> Iterator for WasmSectionIterator<'file> { type Item = WasmSection<'file>; fn next(&mut self) -> Option { self.sections.next().map(|(index, section)| WasmSection { index: SectionIndex(index), section, }) } } impl<'file> ObjectSection<'static> for WasmSection<'file> { type RelocationIterator = WasmRelocationIterator; #[inline] fn index(&self) -> SectionIndex { self.index } #[inline] fn address(&self) -> u64 { 1 } #[inline] fn size(&self) -> u64 { serialize_to_cow(self.section.clone()).map_or(0, |b| b.len() as u64) } #[inline] fn align(&self) -> u64 { 1 } fn data(&self) -> Cow<'static, [u8]> { match *self.section { elements::Section::Custom(ref section) => Some(section.payload().to_vec().into()), elements::Section::Start(section) => { serialize_to_cow(elements::VarUint32::from(section)) } _ => serialize_to_cow(self.section.clone()), } .unwrap_or_else(|| Cow::from(&[][..])) } fn data_range(&self, _address: u64, _size: u64) -> Option<&'static [u8]> { unimplemented!() } #[inline] fn uncompressed_data(&self) -> Cow<'static, [u8]> { // TODO: does wasm support compression? self.data() } fn name(&self) -> Option<&str> { match *self.section { elements::Section::Unparsed { .. } => None, elements::Section::Custom(ref c) => Some(c.name()), elements::Section::Type(_) => Some("Type"), elements::Section::Import(_) => Some("Import"), elements::Section::Function(_) => Some("Function"), elements::Section::Table(_) => Some("Table"), elements::Section::Memory(_) => Some("Memory"), elements::Section::Global(_) => Some("Global"), elements::Section::Export(_) => Some("Export"), elements::Section::Start(_) => Some("Start"), elements::Section::Element(_) => Some("Element"), elements::Section::Code(_) => Some("Code"), elements::Section::DataCount(_) => Some("DataCount"), elements::Section::Data(_) => Some("Data"), elements::Section::Name(_) => Some("Name"), elements::Section::Reloc(_) => Some("Reloc"), } } #[inline] fn segment_name(&self) -> Option<&str> { None } fn kind(&self) -> SectionKind { match *self.section { elements::Section::Unparsed { .. } => SectionKind::Unknown, elements::Section::Custom(_) => SectionKind::Unknown, elements::Section::Type(_) => SectionKind::Other, elements::Section::Import(_) => SectionKind::Other, elements::Section::Function(_) => SectionKind::Other, elements::Section::Table(_) => SectionKind::Other, elements::Section::Memory(_) => SectionKind::Other, elements::Section::Global(_) => SectionKind::Other, elements::Section::Export(_) => SectionKind::Other, elements::Section::Start(_) => SectionKind::Other, elements::Section::Element(_) => SectionKind::Other, elements::Section::Code(_) => SectionKind::Text, elements::Section::DataCount(_) => SectionKind::Other, elements::Section::Data(_) => SectionKind::Data, elements::Section::Name(_) => SectionKind::Other, elements::Section::Reloc(_) => SectionKind::Other, } } fn relocations(&self) -> WasmRelocationIterator { WasmRelocationIterator } } impl<'file> Iterator for WasmSymbolIterator<'file> { type Item = (SymbolIndex, Symbol<'static>); fn next(&mut self) -> Option { unimplemented!() } } impl Iterator for WasmRelocationIterator { type Item = (u64, Relocation); fn next(&mut self) -> Option { None } } object-0.12.0/.cargo_vcs_info.json0000644000000001120000000000000124130ustar00{ "git": { "sha1": "10be0ee1b41338f36c49a47799d7674fefbe5452" } }