object-0.36.5/.cargo_vcs_info.json0000644000000001360000000000100123750ustar { "git": { "sha1": "016cf8d34f257725e35190a642eeb5d6c7c3cb96" }, "path_in_vcs": "" }object-0.36.5/CHANGELOG.md000064400000000000000000001035661046102023000130110ustar 00000000000000# `object` Change Log -------------------------------------------------------------------------------- ## 0.36.5 Released 2024/10/04. ### Added * Added `Architecture::E2K32` and `Architecture::E2K64`. [#727](https://github.com/gimli-rs/object/pull/727) * Added read and write support for `pe::IMAGE_REL_ARM64_BRANCH26`. [#731](https://github.com/gimli-rs/object/pull/731) ### Changed * Fixed decompression of multi-frame Zstandard data in `read::CompressedData::decompress`. [#730](https://github.com/gimli-rs/object/pull/730) -------------------------------------------------------------------------------- ## 0.36.4 Released 2024/08/30. ### Added * Added `pe::IMAGE_FILE_MACHINE_ARM64X` and `pe::IMAGE_FILE_MACHINE_CHPE_X86`. [#717](https://github.com/gimli-rs/object/pull/717) * Added `elf::SHF_GNU_RETAIN` and `elf::SHF_GNU_MBIND`. [#720](https://github.com/gimli-rs/object/pull/720) ### Changed * Fixed the checksum for COFF BSS section symbols in `write::Object`. [#718](https://github.com/gimli-rs/object/pull/718) * Changed `read::CompressedData::decompress` to validate the decompressed size. [#723](https://github.com/gimli-rs/object/pull/723) * Updated `wasmparser` dependency. -------------------------------------------------------------------------------- ## 0.36.3 Released 2024/08/07. ### Added * Added `Iterator` implementations for various types in the low level read API. [#713](https://github.com/gimli-rs/object/pull/713) [#714](https://github.com/gimli-rs/object/pull/714) ### Changed * Changed `from_bytes` constructors for integer endian types to `const`. [#712](https://github.com/gimli-rs/object/pull/712) * Changed `next` methods in the low level read API to fuse after returning an error. [#714](https://github.com/gimli-rs/object/pull/714) * Updated `wasmparser` dependency. [#715](https://github.com/gimli-rs/object/pull/715) -------------------------------------------------------------------------------- ## 0.36.2 Released 2024/07/24. ### Changed * Improved writing of GNU symbol versioning in `build::elf::Builder`. [#705](https://github.com/gimli-rs/object/pull/705) * Fixed alignment of `SHT_HASH`/`SHT_GNU_verdef`/`SHT_GNU_verneed` sections in `write::elf::Writer`. [#706](https://github.com/gimli-rs/object/pull/706) * Fixed writing of GNU hash for absolute symbols in `build::elf::Builder`. [#707](https://github.com/gimli-rs/object/pull/707) * Fixed writing of empty ELF string table in `write::Object`. [#710](https://github.com/gimli-rs/object/pull/710) -------------------------------------------------------------------------------- ## 0.36.1 Released 2024/06/29. ### Added * Added `SectionKind::DebugString`. [#694](https://github.com/gimli-rs/object/pull/694) * Added `Architecture::Sparc` and `Architecture::Sparc32Plus`. [#699](https://github.com/gimli-rs/object/pull/699) [#700](https://github.com/gimli-rs/object/pull/700) * Added more RISC-V ELF relocation constants. [#701](https://github.com/gimli-rs/object/pull/701) ### Changed * Changed `read::ElfFile::imports` to return the library for versioned symbols. [#693](https://github.com/gimli-rs/object/pull/693) * Changed `read::MachOFile` to support Go's debug section compression. [#697](https://github.com/gimli-rs/object/pull/697) * Reversed the order of Mach-O relocations emitted by `write::Object`. [#702](https://github.com/gimli-rs/object/pull/702) -------------------------------------------------------------------------------- ## 0.36.0 Released 2024/05/26. ### Breaking changes * Deleted `data` and `align` parameters for `write::Object::add_subsection`. Use `add_symbol_data` or `add_symbol_bss` instead. [#676](https://github.com/gimli-rs/object/pull/676) * Changed methods in the lower level read API to accept or return `SectionIndex` or `SymbolIndex` instead of `usize`. [#677](https://github.com/gimli-rs/object/pull/677) [#684](https://github.com/gimli-rs/object/pull/684) [#685](https://github.com/gimli-rs/object/pull/685) * Deleted `SymbolKind::Null`. Changed `read::Object::sections` and `read::Object::symbols` to no longer return null entries. This affects ELF and XCOFF. [#679](https://github.com/gimli-rs/object/pull/679) * Changed `read::ObjectMap::object` to return `ObjectMapFile`. This handles splitting the object file name into path and member. [#686](https://github.com/gimli-rs/object/pull/686) * Changed `read::coff::ImageSymbol::address` to only return an address for symbols that have an address. [#689](https://github.com/gimli-rs/object/pull/689) ### Added * Added `pod::slice_from_all_bytes` and `pod::slice_from_all_bytes_mut`. [#672](https://github.com/gimli-rs/object/pull/672) * Added `write::Object::set_subsections_via_symbols`. Changed `write::Object::add_symbol_data` and `write::Object::add_symbol_bss` to correctly handle zero size symbols when subsections are enabled. [#676](https://github.com/gimli-rs/object/pull/676) * Added methods in the unified read API to return the lower level API structures. Some existing methods were deprecated so that naming of these methods is more consistent. [#678](https://github.com/gimli-rs/object/pull/678) * Added methods in the lower level read API to return a `SectionIndex` or `SymbolIndex`. [#684](https://github.com/gimli-rs/object/pull/684) [#689](https://github.com/gimli-rs/object/pull/689) * Implemented `Display` for `read::SymbolIndex` and `read::SectionIndex`. [#684](https://github.com/gimli-rs/object/pull/684) * Added `is_common`, `is_absolute`, `is_local`, and `is_weak` to `read::elf::Sym`. [#685](https://github.com/gimli-rs/object/pull/685) ### Changed * Changed `read::ArchiveFile` to skip the `` member. [#669](https://github.com/gimli-rs/object/pull/669) * Fixed handling of segment data in the dyld shared cache. [#673](https://github.com/gimli-rs/object/pull/673) * Changed `read::RelocationMap` to handle Mach-O section relocations. [#675](https://github.com/gimli-rs/object/pull/675) * Changed `read::elf::RelocationSections` to ignore relocations that apply to relocations. [#680](https://github.com/gimli-rs/object/pull/680) * Removed a lifetime bound from an argument in `read::elf::SectionTable::section_name`, `read::elf::SymbolTable::symbol_name`, and `read::elf::SymbolTable::symbol_section`. [#681](https://github.com/gimli-rs/object/pull/681) -------------------------------------------------------------------------------- ## 0.35.0 Released 2024/04/10. ### Breaking changes * Moved the `'file` lifetime parameter from `read::Object` to its associated types. [#655](https://github.com/gimli-rs/object/pull/655) ### Added * Added support more section kinds in `build::elf`. [#650](https://github.com/gimli-rs/object/pull/650) * Added thin archive support to `read::ArchiveFile`. [#651](https://github.com/gimli-rs/object/pull/651) * Added `read::ReadCacheOps` and changed `read::ReadCache` bound from `Read + Seek` to `ReadCacheOps`. [#652](https://github.com/gimli-rs/object/pull/652) * Added `read::ObjectSection::relocation_map` [#654](https://github.com/gimli-rs/object/pull/654) * Added `read::ArchiveFile::symbols`. [#658](https://github.com/gimli-rs/object/pull/658) * Added `BinaryFormat::native_object`. [#661](https://github.com/gimli-rs/object/pull/661) ### Changed * The minimum supported rust version for the `read` feature and its dependencies has changed to 1.65.0. [#655](https://github.com/gimli-rs/object/pull/655) * Fixed `sh_offset` handling for `SHT_NOBITS` sections in `build::elf`. [#645](https://github.com/gimli-rs/object/pull/645) * Fixed handling of ELF files with dynamic symbols but no dynamic strings. [#646](https://github.com/gimli-rs/object/pull/646) * Fixed potential panics in `read::WasmFile` due to invalid function indices. [#649](https://github.com/gimli-rs/object/pull/649) * Fixed handling of Wasm components in `read::WasmFile`. [#649](https://github.com/gimli-rs/object/pull/649) * Fixed `sh_entsize` for 32-bit hash sections in `write::elf`. [#650](https://github.com/gimli-rs/object/pull/650) * Fixed `sh_size` for attribute sections in `build::elf`. [#650](https://github.com/gimli-rs/object/pull/650) * Fixed `sh_info` for `SHT_DYNSYM` sections in `build::elf`. [#650](https://github.com/gimli-rs/object/pull/650) * Fixed handling of dynamic relocations with invalid `sh_link` in `build::elf`. [#650](https://github.com/gimli-rs/object/pull/650) * Fixed parsing of member names containing '/' in `read::ArchiveFile`. [#657](https://github.com/gimli-rs/object/pull/657) * Fixed handling of load segment alignments in `build::elf::Builder::read`. [#659](https://github.com/gimli-rs/object/pull/659) -------------------------------------------------------------------------------- ## 0.34.0 Released 2024/03/11. ### Breaking changes * Replaced `macho::DyldSubCacheInfo` with `macho::DyldSubCacheEntryV1`. Changed the return type of `macho::DyldCacheHeader::subcaches`. [#642](https://github.com/gimli-rs/object/pull/642) ### Changed * Added `macho::DyldSubCacheEntryV2` and changed `read::macho::DyldCache` to handle both versions. This is needed for macOS 13 and above. [#642](https://github.com/gimli-rs/object/pull/642) -------------------------------------------------------------------------------- ## 0.33.0 Released 2024/03/05. ### Breaking changes * Deleted file format variants in `RelocationKind`. Replaced their usage with `read::Relocation::flags` and `write::Relocation::flags`. [#585](https://github.com/gimli-rs/object/pull/585) * Replaced `kind`, `encoding` and `size` fields in `write::Relocation` with `RelocationFlags::Generic` in the `flags` field. [#585](https://github.com/gimli-rs/object/pull/585) * Replaced `macho::FatHeader::parse`, `macho::FatHeader::parse_arch32`, and `macho::FatHeader::parse_arch64` with `read::macho::MachOFatFile`, `read::macho::MachOFatFile32` and `read::macho::MachOFatFile64`. [#623](https://github.com/gimli-rs/object/pull/623) ### Added * Added `macho::PLATFORM_XROS` and `macho::PLATFORM_XROSSIMULATOR`. [#626](https://github.com/gimli-rs/object/pull/626) * Added `build::elf::Builder` and associated types. Extended `write::elf::Writer` to support this. [#618](https://github.com/gimli-rs/object/pull/618) ### Changed * Changed the lifetime to `'data` for the return value of `ObjectSection::name`, `ObjectSection::name_bytes`, `ObjectComdat::name`, `ObjectComdat::name_bytes`. [#620](https://github.com/gimli-rs/object/pull/620) [#622](https://github.com/gimli-rs/object/pull/622) * Checked that sizes are smaller than the file length in `read::ReadCache`. [#630](https://github.com/gimli-rs/object/pull/630) * Used `Vec::try_reserve_exact` for large allocations. [#632](https://github.com/gimli-rs/object/pull/632) -------------------------------------------------------------------------------- ## 0.32.2 Released 2023/12/24. ### Added * Added ELF relocations for LoongArch ABI v2.20. [#578](https://github.com/gimli-rs/object/pull/578) [#589](https://github.com/gimli-rs/object/pull/589) * Added ELF support for SHARC. [#593](https://github.com/gimli-rs/object/pull/593) * Added `write::coff::Writer`. [#595](https://github.com/gimli-rs/object/pull/595) * Added `SubArchitecture::Arm64EC` support for PE/COFF. [#607](https://github.com/gimli-rs/object/pull/607) * Added `SubArchitecture::Arm64E` support for Mach-O. [#614](https://github.com/gimli-rs/object/pull/614) * Added `read::Object::symbol_by_name` and `read::Object::symbol_by_name_bytes`. [#602](https://github.com/gimli-rs/object/pull/602) * Added more functions to the low level API in `read::xcoff`. [#608](https://github.com/gimli-rs/object/pull/608) * Added more functions to the low level API in `read::macho`. [#584](https://github.com/gimli-rs/object/pull/584) ### Changed * Fixes for AArch64 relocation addends for Mach-O. [#581](https://github.com/gimli-rs/object/pull/581) * Changes to `write::Object` output for Mach-O, including the addition of a `LC_DYSYMTAB` load command. [#584](https://github.com/gimli-rs/object/pull/584) * Changed `write::Object` to always use `R_X86_64_PLT32` for x86-64 branches for ELF. [#590](https://github.com/gimli-rs/object/pull/590) * Fixed `read::ObjectSymbol::kind` for undefined section symbols for COFF. [#592](https://github.com/gimli-rs/object/pull/592) * Fixed `write::Object` to accept undefined section symbols for COFF. [#594](https://github.com/gimli-rs/object/pull/594) * Improved parsing of auxiliary section symbols for COFF. [#603](https://github.com/gimli-rs/object/pull/603) * Improved the selection of symbols for `read::Object::symbol_map`. This includes changes to `read::Symbol::is_definition`. [#601](https://github.com/gimli-rs/object/pull/601) [#606](https://github.com/gimli-rs/object/pull/606) * Changed `read::ObjectSymbol::kind` for ELF `STT_NOTYPE` symbols to `SymbolKind::Unknown`. [#604](https://github.com/gimli-rs/object/pull/604) * Changed `read::ObjectSymbol::scope` for XCOFF `C_HIDEXT` symbols to `SymbolScope::Compilation`. [#605](https://github.com/gimli-rs/object/pull/605) -------------------------------------------------------------------------------- ## 0.32.1 Released 2023/09/03. ### Added * Added `write::Object::set_macho_cpu_subtype`. [#574](https://github.com/gimli-rs/object/pull/574) -------------------------------------------------------------------------------- ## 0.32.0 Released 2023/08/12. ### Breaking changes * Changed `read::elf::Note::name` to exclude all trailing null bytes. [#549](https://github.com/gimli-rs/object/pull/549) * Updated dependencies, and changed some optional dependencies to use the `dep:` feature syntax. [#558](https://github.com/gimli-rs/object/pull/558) [#569](https://github.com/gimli-rs/object/pull/569) ### Changed * The minimum supported rust version for the `read` feature and its dependencies has changed to 1.60.0. * The minimum supported rust version for other features has changed to 1.65.0. * Changed many definitions from `static` to `const`. [#549](https://github.com/gimli-rs/object/pull/549) * Fixed Mach-O section alignment padding in `write::Object`. [#553](https://github.com/gimli-rs/object/pull/553) * Changed `read::File` to an enum. [#564](https://github.com/gimli-rs/object/pull/564) ### Added * Added `elf::ELF_NOTE_GO`, `elf::NT_GO_BUILD_ID`, and `read::elf::Note::name_bytes`. [#549](https://github.com/gimli-rs/object/pull/549) * Added `read::FileKind::CoffImport` and `read::coff::ImportFile`. [#555](https://github.com/gimli-rs/object/pull/555) [#556](https://github.com/gimli-rs/object/pull/556) * Added `Architecture::Csky` and basic ELF support for C-SKY. [#561](https://github.com/gimli-rs/object/pull/561) * Added `read::elf::ElfSymbol::raw_symbol`. [#562](https://github.com/gimli-rs/object/pull/562) -------------------------------------------------------------------------------- ## 0.30.4 Released 2023/06/05. ### Changed * Fixed Mach-O section alignment padding in `write::Object`. [#553](https://github.com/gimli-rs/object/pull/553) -------------------------------------------------------------------------------- ## 0.31.1 Released 2023/05/09. ### Changed * Fixed address for global symbols in `read::wasm`. [#539](https://github.com/gimli-rs/object/pull/539) * Fixed writing of alignment for empty ELF sections. [#540](https://github.com/gimli-rs/object/pull/540) ### Added * Added more `elf::GNU_PROPERTY_*` definitions. Added `read::elf::note::gnu_properties`, `write::StandardSection::GnuProperty`, and `write::Object::add_elf_gnu_property_u32`. [#537](https://github.com/gimli-rs/object/pull/537) [#541](https://github.com/gimli-rs/object/pull/541) * Added Mach-O support for `Architecture::Aarch64_Ilp32`. [#542](https://github.com/gimli-rs/object/pull/542) [#545](https://github.com/gimli-rs/object/pull/545) * Added `Architecture::Wasm64`. [#543](https://github.com/gimli-rs/object/pull/543) -------------------------------------------------------------------------------- ## 0.31.0 Released 2023/04/14. ### Breaking changes * Added a type parameter on existing COFF types to support reading COFF `/bigobj` files. [#502](https://github.com/gimli-rs/object/pull/502) * Changed PE symbols to support COFF `/bigobj`. Changed `pe::IMAGE_SYM_*` to `i32`. Changed `pe::ImageSymbolEx::section_number` to `I32Bytes`. Deleted a number of methods from `pe::ImageSymbol`. Use the `read::pe::ImageSymbol` trait instead. [#502](https://github.com/gimli-rs/object/pull/502) * Changed `pe::Guid` to a single array, and added methods to read the individual fields. [#502](https://github.com/gimli-rs/object/pull/502) * Added `Symbol` type parameter to `SymbolFlags` to support `SymbolFlags::Xcoff`. [#527](https://github.com/gimli-rs/object/pull/527) ### Changed * Fix alignment when reserving zero length sections in `write::elf::Write::reserve`. [#514](https://github.com/gimli-rs/object/pull/514) * Validate command size in `read::macho::LoadCommandIterator`. [#516](https://github.com/gimli-rs/object/pull/516) * Handle invalid alignment in `read::macho::MachoSection::align`. [#516](https://github.com/gimli-rs/object/pull/516) * Accept `SymbolKind::Unknown` in `write::Object::macho_write`. [#519](https://github.com/gimli-rs/object/pull/519) * Updated `wasmparser` dependency. [#528](https://github.com/gimli-rs/object/pull/528) ### Added * Added more `elf::EF_RISCV_*` definitions. [#507](https://github.com/gimli-rs/object/pull/507) * Added `read::elf::SectionHeader::gnu_attributes` and associated types. Added `.gnu.attributes` support to `write::elf::Writer`. [#509](https://github.com/gimli-rs/object/pull/509) [#525](https://github.com/gimli-rs/object/pull/525) * Added `write::Object::set_macho_build_version`. [#524](https://github.com/gimli-rs/object/pull/524) * Added `read::FileKind::Xcoff32`, `read::FileKind::Xcoff64`, `read::XcoffFile`, and associated types. Added XCOFF support to `write::Object`. [#469](https://github.com/gimli-rs/object/pull/469) [#476](https://github.com/gimli-rs/object/pull/476) [#477](https://github.com/gimli-rs/object/pull/477) [#482](https://github.com/gimli-rs/object/pull/482) [#484](https://github.com/gimli-rs/object/pull/484) [#486](https://github.com/gimli-rs/object/pull/486) [#527](https://github.com/gimli-rs/object/pull/527) * Added `read::FileKind::CoffBig`, `read::pe::CoffHeader` and `read::pe::ImageSymbol`. [#502](https://github.com/gimli-rs/object/pull/502) * Added `elf::PT_GNU_PROPERTY`. [#530](https://github.com/gimli-rs/object/pull/530) * Added `elf::ELFCOMPRESS_ZSTD`, `read::CompressionFormat::Zstandard`, and Zstandard decompression in `read::CompressedData::decompress` using the `ruzstd` crate. [#532](https://github.com/gimli-rs/object/pull/532) * Added `read::elf::NoteIterator::new`. [#533](https://github.com/gimli-rs/object/pull/533) -------------------------------------------------------------------------------- ## 0.30.3 Released 2023/01/23. ### Added * Added `SectionKind::ReadOnlyDataWithRel` for writing. [#504](https://github.com/gimli-rs/object/pull/504) -------------------------------------------------------------------------------- ## 0.30.2 Released 2023/01/11. ### Added * Added more ELF constants for AVR flags and relocations. [#500](https://github.com/gimli-rs/object/pull/500) -------------------------------------------------------------------------------- ## 0.30.1 Released 2023/01/04. ### Changed * Changed `read::ElfSymbol::kind` to handle `STT_NOTYPE` and `STT_GNU_IFUNC`. [#498](https://github.com/gimli-rs/object/pull/498) ### Added * Added `read::CoffSymbol::raw_symbol`. [#494](https://github.com/gimli-rs/object/pull/494) * Added ELF support for Solana Binary Format. [#491](https://github.com/gimli-rs/object/pull/491) * Added ELF support for AArch64 ILP32. [#497](https://github.com/gimli-rs/object/pull/497) -------------------------------------------------------------------------------- ## 0.30.0 Released 2022/11/22. ### Breaking changes * The minimum supported rust version for the `read` feature has changed to 1.52.0. [#458](https://github.com/gimli-rs/object/pull/458) * The minimum supported rust version for the `write` feature has changed to 1.61.0. * Fixed endian handling in `read::elf::SymbolTable::shndx`. [#458](https://github.com/gimli-rs/object/pull/458) * Fixed endian handling in `read::pe::ResourceName`. [#458](https://github.com/gimli-rs/object/pull/458) * Changed definitions for LoongArch ELF header flags. [#483](https://github.com/gimli-rs/object/pull/483) ### Changed * Fixed parsing of multiple debug directory entries in `read::pe::PeFile::pdb_info`. [#451](https://github.com/gimli-rs/object/pull/451) * Changed the section name used when writing COFF stub symbols. [#475](https://github.com/gimli-rs/object/pull/475) ### Added * Added `read::pe::DataDirectories::delay_load_import_table`. [#448](https://github.com/gimli-rs/object/pull/448) * Added `read::macho::LoadCommandData::raw_data`. [#449](https://github.com/gimli-rs/object/pull/449) * Added ELF relocations for LoongArch ps ABI v2. [#450](https://github.com/gimli-rs/object/pull/450) * Added PowerPC support for Mach-O. [#460](https://github.com/gimli-rs/object/pull/460) * Added support for reading the AIX big archive format. [#462](https://github.com/gimli-rs/object/pull/462) [#467](https://github.com/gimli-rs/object/pull/467) [#473](https://github.com/gimli-rs/object/pull/473) * Added support for `RelocationEncoding::AArch64Call` when writing Mach-O files. [#465](https://github.com/gimli-rs/object/pull/465) * Added support for `RelocationKind::Relative` when writing RISC-V ELF files. [#470](https://github.com/gimli-rs/object/pull/470) * Added Xtensa architecture support for ELF. [#481](https://github.com/gimli-rs/object/pull/481) * Added `read::pe::ResourceName::raw_data`. [#487](https://github.com/gimli-rs/object/pull/487) -------------------------------------------------------------------------------- ## 0.29.0 Released 2022/06/22. ### Breaking changes * The `write` feature now has a minimum supported rust version of 1.56.1. [#444](https://github.com/gimli-rs/object/pull/444) * Added `os_abi` and `abi_version` fields to `FileFlags::Elf`. [#438](https://github.com/gimli-rs/object/pull/438) [#441](https://github.com/gimli-rs/object/pull/441) ### Changed * Fixed handling of empty symbol tables in `read::elf::ElfFile::symbol_table` and `read::elf::ElfFile::dynamic_symbol_table`. [#443](https://github.com/gimli-rs/object/pull/443) ### Added * Added more `ELF_OSABI_*` constants. [#439](https://github.com/gimli-rs/object/pull/439) -------------------------------------------------------------------------------- ## 0.28.4 Released 2022/05/09. ### Added * Added `read::pe::DataDirectories::resource_directory`. [#425](https://github.com/gimli-rs/object/pull/425) [#427](https://github.com/gimli-rs/object/pull/427) * Added PE support for more ARM relocations. [#428](https://github.com/gimli-rs/object/pull/428) * Added support for `Architecture::LoongArch64`. [#430](https://github.com/gimli-rs/object/pull/430) [#432](https://github.com/gimli-rs/object/pull/432) * Added `elf::EF_MIPS_ABI` and associated constants. [#433](https://github.com/gimli-rs/object/pull/433) -------------------------------------------------------------------------------- ## 0.28.3 Released 2022/01/19. ### Changed * For the Mach-O support in `write::Object`, accept `RelocationKind::MachO` for all architectures, and accept `RelocationKind::Absolute` for ARM64. [#422](https://github.com/gimli-rs/object/pull/422) ### Added * Added `pe::ImageDataDirectory::file_range`, `read::pe::SectionTable::pe_file_range_at` and `pe::ImageSectionHeader::pe_file_range_at`. [#421](https://github.com/gimli-rs/object/pull/421) * Added `write::Object::add_coff_exports`. [#423](https://github.com/gimli-rs/object/pull/423) -------------------------------------------------------------------------------- ## 0.28.2 Released 2022/01/09. ### Changed * Ignored errors for the Wasm extended name section in `read::WasmFile::parse`. [#408](https://github.com/gimli-rs/object/pull/408) * Ignored errors for the COFF symbol table in `read::PeFile::parse`. [#410](https://github.com/gimli-rs/object/pull/410) * Fixed handling of `SectionFlags::Coff` in `write::Object::coff_write`. [#412](https://github.com/gimli-rs/object/pull/412) ### Added * Added `read::ObjectSegment::flags`. [#416](https://github.com/gimli-rs/object/pull/416) [#418](https://github.com/gimli-rs/object/pull/418) -------------------------------------------------------------------------------- ## 0.28.1 Released 2021/12/12. ### Changed * Fixed `read::elf::SymbolTable::shndx_section`. [#405](https://github.com/gimli-rs/object/pull/405) * Fixed build warnings. [#405](https://github.com/gimli-rs/object/pull/405) [#406](https://github.com/gimli-rs/object/pull/406) -------------------------------------------------------------------------------- ## 0.28.0 Released 2021/12/12. ### Breaking changes * `write_core` feature no longer enables `std` support. Use `write_std` instead. [#400](https://github.com/gimli-rs/object/pull/400) * Multiple changes related to Mach-O split dyld cache support. [#398](https://github.com/gimli-rs/object/pull/398) ### Added * Added `write::pe::Writer::write_file_align`. [#397](https://github.com/gimli-rs/object/pull/397) * Added support for Mach-O split dyld cache. [#398](https://github.com/gimli-rs/object/pull/398) * Added support for `IMAGE_SCN_LNK_NRELOC_OVFL` when reading and writing COFF. [#399](https://github.com/gimli-rs/object/pull/399) * Added `write::elf::Writer::reserve_null_symbol_index`. [#402](https://github.com/gimli-rs/object/pull/402) -------------------------------------------------------------------------------- ## 0.27.1 Released 2021/10/22. ### Changed * Fixed build error with older Rust versions due to cargo resolver version. -------------------------------------------------------------------------------- ## 0.27.0 Released 2021/10/17. ### Breaking changes * Changed `read::elf` to use `SectionIndex` instead of `usize` in more places. [#341](https://github.com/gimli-rs/object/pull/341) * Changed some `read::elf` section methods to additionally return the linked section index. [#341](https://github.com/gimli-rs/object/pull/341) * Changed `read::pe::ImageNtHeaders::parse` to return `DataDirectories` instead of a slice. [#357](https://github.com/gimli-rs/object/pull/357) * Deleted `value` parameter for `write:WritableBuffer::resize`. [#369](https://github.com/gimli-rs/object/pull/369) * Changed `write::Object` and `write::Section` to use `Cow` for section data. This added a lifetime parameter, which existing users can set to `'static`. [#370](https://github.com/gimli-rs/object/pull/370) ### Changed * Fixed parsing when PE import directory has zero size. [#341](https://github.com/gimli-rs/object/pull/341) * Fixed parsing when PE import directory has zero for original first thunk. [#385](https://github.com/gimli-rs/object/pull/385) [#387](https://github.com/gimli-rs/object/pull/387) * Fixed parsing when PE export directory has zero number of names. [#353](https://github.com/gimli-rs/object/pull/353) * Fixed parsing when PE export directory has zero number of names and addresses. [#362](https://github.com/gimli-rs/object/pull/362) * Fixed parsing when PE sections are contiguous. [#354](https://github.com/gimli-rs/object/pull/354) * Fixed `std` feature for `indexmap` dependency. [#374](https://github.com/gimli-rs/object/pull/374) * Fixed overflow in COFF section name offset parsing. [#390](https://github.com/gimli-rs/object/pull/390) ### Added * Added `name_bytes` methods to unified `read` traits. [#351](https://github.com/gimli-rs/object/pull/351) * Added `read::Object::kind`. [#352](https://github.com/gimli-rs/object/pull/352) * Added `read::elf::VersionTable` and related helpers. [#341](https://github.com/gimli-rs/object/pull/341) * Added `read::elf::SectionTable::dynamic` and related helpers. [#345](https://github.com/gimli-rs/object/pull/345) * Added `read::coff::SectionTable::max_section_file_offset`. [#344](https://github.com/gimli-rs/object/pull/344) * Added `read::pe::ExportTable` and related helpers. [#349](https://github.com/gimli-rs/object/pull/349) [#353](https://github.com/gimli-rs/object/pull/353) * Added `read::pe::ImportTable` and related helpers. [#357](https://github.com/gimli-rs/object/pull/357) * Added `read::pe::DataDirectories` and related helpers. [#357](https://github.com/gimli-rs/object/pull/357) [#384](https://github.com/gimli-rs/object/pull/384) * Added `read::pe::RichHeaderInfo` and related helpers. [#375](https://github.com/gimli-rs/object/pull/375) [#379](https://github.com/gimli-rs/object/pull/379) * Added `read::pe::RelocationBlocks` and related helpers. [#378](https://github.com/gimli-rs/object/pull/378) * Added `write::elf::Writer`. [#350](https://github.com/gimli-rs/object/pull/350) * Added `write::pe::Writer`. [#382](https://github.com/gimli-rs/object/pull/382) [#388](https://github.com/gimli-rs/object/pull/388) * Added `write::Section::data/data_mut`. [#367](https://github.com/gimli-rs/object/pull/367) * Added `write::Object::write_stream`. [#369](https://github.com/gimli-rs/object/pull/369) * Added MIPSr6 ELF header flag definitions. [#372](https://github.com/gimli-rs/object/pull/372) -------------------------------------------------------------------------------- ## 0.26.2 Released 2021/08/28. ### Added * Added support for 64-bit symbol table names to `read::archive`. [#366](https://github.com/gimli-rs/object/pull/366) -------------------------------------------------------------------------------- ## 0.26.1 Released 2021/08/19. ### Changed * Activate `memchr`'s `rustc-dep-of-std` feature [#356](https://github.com/gimli-rs/object/pull/356) -------------------------------------------------------------------------------- ## 0.26.0 Released 2021/07/26. ### Breaking changes * Changed `ReadRef::read_bytes_at_until` to accept a range parameter. [#326](https://github.com/gimli-rs/object/pull/326) * Added `ReadRef` type parameter to `read::StringTable` and types that contain it. String table entries are now only read as required. [#326](https://github.com/gimli-rs/object/pull/326) * Changed result type of `read::elf::SectionHeader::data` and `data_as_array`. [#332](https://github.com/gimli-rs/object/pull/332) * Moved `pod::WritableBuffer` to `write::WritableBuffer`. Renamed `WritableBuffer::extend` to `write_bytes`. Added more provided methods to `WritableBuffer`. [#335](https://github.com/gimli-rs/object/pull/335) * Moved `pod::Bytes` to `read::Bytes`. [#336](https://github.com/gimli-rs/object/pull/336) * Added `is_mips64el` parameter to `elf::Rela64::r_info/set_r_info`. [#337](https://github.com/gimli-rs/object/pull/337) ### Changed * Removed `alloc` dependency when no features are enabled. [#336](https://github.com/gimli-rs/object/pull/336) ### Added * Added `read::pe::PeFile` methods: `section_table`, `data_directory`, and `data`. [#324](https://github.com/gimli-rs/object/pull/324) * Added more ELF definitions. [#332](https://github.com/gimli-rs/object/pull/332) * Added `read::elf::SectionTable` methods for hash tables and symbol version information. [#332](https://github.com/gimli-rs/object/pull/332) * Added PE RISC-V definitions. [#333](https://github.com/gimli-rs/object/pull/333) * Added `WritableBuffer` implementation for `Vec`. [#335](https://github.com/gimli-rs/object/pull/335) -------------------------------------------------------------------------------- ## 0.25.3 Released 2021/06/12. ### Added * Added `RelocationEncoding::AArch64Call`. [#322](https://github.com/gimli-rs/object/pull/322) -------------------------------------------------------------------------------- ## 0.25.2 Released 2021/06/04. ### Added * Added `Architecture::X86_64_X32`. [#320](https://github.com/gimli-rs/object/pull/320) -------------------------------------------------------------------------------- ## 0.25.1 Released 2021/06/03. ### Changed * write: Fix choice of `SHT_REL` or `SHT_RELA` for most architectures. [#318](https://github.com/gimli-rs/object/pull/318) * write: Fix relocation encoding for MIPS64EL. [#318](https://github.com/gimli-rs/object/pull/318) -------------------------------------------------------------------------------- ## 0.25.0 Released 2021/06/02. ### Breaking changes * Added `non_exhaustive` to most public enums. [#306](https://github.com/gimli-rs/object/pull/306) * `MachHeader::parse` and `MachHeader::load_commands` now require a header offset. [#304](https://github.com/gimli-rs/object/pull/304) * Added `ReadRef::read_bytes_at_until`. [#308](https://github.com/gimli-rs/object/pull/308) * `PeFile::entry`, `PeSection::address` and `PeSegment::address` now return a virtual address instead of a RVA. [#315](https://github.com/gimli-rs/object/pull/315) ### Added * Added `pod::from_bytes_mut`, `pod::slice_from_bytes_mut`, `pod::bytes_of_mut`, and `pod::bytes_of_slice_mut`. [#296](https://github.com/gimli-rs/object/pull/296) [#297](https://github.com/gimli-rs/object/pull/297) * Added `Object::pdb_info`. [#298](https://github.com/gimli-rs/object/pull/298) * Added `read::macho::DyldCache`, other associated definitions, and support for these in the examples. [#308](https://github.com/gimli-rs/object/pull/308) * Added more architecture support. [#303](https://github.com/gimli-rs/object/pull/303) [#309](https://github.com/gimli-rs/object/pull/309) * Derive more traits for enums. [#311](https://github.com/gimli-rs/object/pull/311) * Added `Object::relative_address_base`. [#315](https://github.com/gimli-rs/object/pull/315) ### Changed * Improved performance for string parsing. [#302](https://github.com/gimli-rs/object/pull/302) * `objdump` example allows selecting container members. [#308](https://github.com/gimli-rs/object/pull/308) object-0.36.5/Cargo.toml0000644000000062760000000000100104060ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" rust-version = "1.65" name = "object" version = "0.36.5" build = false include = [ "/Cargo.toml", "/CHANGELOG.md", "/README.md", "/LICENSE-APACHE", "/LICENSE-MIT", "/src", "/tests", ] autobins = false autoexamples = false autotests = false autobenches = false description = "A unified interface for reading and writing object file formats." readme = "README.md" keywords = [ "object", "elf", "mach-o", "pe", "coff", ] license = "Apache-2.0 OR MIT" repository = "https://github.com/gimli-rs/object" resolver = "2" [package.metadata.docs.rs] features = ["doc"] [lib] name = "object" path = "src/lib.rs" [[test]] name = "integration" path = "tests/integration.rs" [[test]] name = "parse_self" path = "tests/parse_self.rs" [dependencies.alloc] version = "1.0.0" optional = true package = "rustc-std-workspace-alloc" [dependencies.compiler_builtins] version = "0.1.2" optional = true [dependencies.core] version = "1.0.0" optional = true package = "rustc-std-workspace-core" [dependencies.crc32fast] version = "1.2" optional = true default-features = false [dependencies.flate2] version = "1" optional = true [dependencies.hashbrown] version = "0.15.0" features = ["default-hasher"] optional = true default-features = false [dependencies.indexmap] version = "2.0" optional = true default-features = false [dependencies.memchr] version = "2.4.1" default-features = false [dependencies.ruzstd] version = "0.7.0" optional = true [dependencies.wasmparser] version = "0.218.0" optional = true default-features = false [features] all = [ "read", "write", "build", "std", "compression", "wasm", ] archive = [] build = [ "build_core", "write_std", "elf", ] build_core = [ "read_core", "write_core", ] cargo-all = [] coff = [] compression = [ "dep:flate2", "dep:ruzstd", "std", ] default = [ "read", "compression", ] doc = [ "read_core", "write_std", "build_core", "std", "compression", "archive", "coff", "elf", "macho", "pe", "wasm", "xcoff", ] elf = [] macho = [] pe = ["coff"] read = [ "read_core", "archive", "coff", "elf", "macho", "pe", "xcoff", "unaligned", ] read_core = [] rustc-dep-of-std = [ "core", "compiler_builtins", "alloc", "memchr/rustc-dep-of-std", ] std = ["memchr/std"] unaligned = [] unstable = [] unstable-all = [ "all", "unstable", ] wasm = ["dep:wasmparser"] write = [ "write_std", "coff", "elf", "macho", "pe", "xcoff", ] write_core = [ "dep:crc32fast", "dep:indexmap", "dep:hashbrown", ] write_std = [ "write_core", "std", "indexmap?/std", "crc32fast?/std", ] xcoff = [] object-0.36.5/Cargo.toml.orig000075500000000000000000000102711046102023000140600ustar 00000000000000[package] name = "object" version = "0.36.5" edition = "2018" keywords = ["object", "elf", "mach-o", "pe", "coff"] license = "Apache-2.0 OR MIT" repository = "https://github.com/gimli-rs/object" rust-version = "1.65" description = "A unified interface for reading and writing object file formats." include = [ "/Cargo.toml", "/CHANGELOG.md", "/README.md", "/LICENSE-APACHE", "/LICENSE-MIT", "/src", "/tests", ] [package.metadata.docs.rs] features = ['doc'] [dependencies] crc32fast = { version = "1.2", default-features = false, optional = true } flate2 = { version = "1", optional = true } indexmap = { version = "2.0", default-features = false, optional = true } wasmparser = { version = "0.218.0", default-features = false, optional = true } memchr = { version = "2.4.1", default-features = false } hashbrown = { version = "0.15.0", features = ["default-hasher"], default-features = false, optional = true } ruzstd = { version = "0.7.0", optional = true } # Internal feature, only used when building as part of libstd, not part of the # stable interface of this crate. core = { version = '1.0.0', optional = true, package = 'rustc-std-workspace-core' } compiler_builtins = { version = '0.1.2', optional = true } alloc = { version = '1.0.0', optional = true, package = 'rustc-std-workspace-alloc' } [features] #======================================= # Read/write features. # Core read support. You will need to enable some file formats too. read_core = [] # Read support for most file formats (including unaligned files). read = ["read_core", "archive", "coff", "elf", "macho", "pe", "xcoff", "unaligned"] # Core write support. You will need to enable some file formats too. write_core = ["dep:crc32fast", "dep:indexmap", "dep:hashbrown"] # Core write support with libstd features. You will need to enable some file formats too. write_std = ["write_core", "std", "indexmap?/std", "crc32fast?/std"] # Write support for all file formats, including libstd features. write = ["write_std", "coff", "elf", "macho", "pe", "xcoff"] # Core builder support. You will need to enable some file formats too. build_core = ["read_core", "write_core"] # Builder support for all file formats. build = ["build_core", "write_std", "elf"] #======================================= # Misc features. # Enable things that require libstd. # Currently, this provides an `Error` implementation. std = ["memchr/std"] # Enable decompression of compressed sections. # This feature is not required if you want to do your own decompression. compression = ["dep:flate2", "dep:ruzstd", "std"] # Treat all types as unaligned. # Normally types use the alignment required by the specifications, but # sometimes files do not strictly follow the specifications. # This may be useful to enable when processing files for architectures # that have no alignment constraints. unaligned = [] #======================================= # File format features. archive = [] coff = [] elf = [] macho = [] pe = ["coff"] wasm = ["dep:wasmparser"] xcoff = [] #======================================= # By default, support all read features. default = ["read", "compression"] #======================================= # Umbrella feature for enabling all user-facing features of this crate. Does not # enable internal features like `rustc-dep-of-std`. all = ["read", "write", "build", "std", "compression", "wasm"] # Use of --all-features is not supported. # This is a dummy feature to detect when --all-features is used. cargo-all = [] #======================================= # Documentation should be generated with everything in "all" except for "unaligned". doc = [ "read_core", "write_std", "build_core", "std", "compression", "archive", "coff", "elf", "macho", "pe", "wasm", "xcoff", ] #======================================= # Unstable features. Breaking changes in these features will not affect versioning. unstable = [] unstable-all = ["all", "unstable"] #======================================= # Internal feature, only used when building as part of libstd, not part of the # stable interface of this crate. rustc-dep-of-std = ['core', 'compiler_builtins', 'alloc', 'memchr/rustc-dep-of-std'] [workspace] members = ["crates/*"] default-members = [".", "crates/examples"] resolver = "2" object-0.36.5/LICENSE-APACHE000064400000000000000000000251371046102023000131210ustar 00000000000000 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.36.5/LICENSE-MIT000064400000000000000000000020501046102023000126160ustar 00000000000000Copyright (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.36.5/README.md000064400000000000000000000042111046102023000124420ustar 00000000000000# `object` The `object` crate provides a unified interface to working with object files across platforms. It supports reading relocatable object files and executable files, and writing COFF/ELF/Mach-O/XCOFF relocatable object files and ELF/PE executable files. For reading files, it provides multiple levels of support: * raw struct definitions suitable for zero copy access * low level APIs for accessing the raw structs ([example](crates/examples/src/readobj/)) * a higher level unified API for accessing common features of object files, such as sections and symbols ([example](crates/examples/src/objdump.rs)) Supported file formats for reading: ELF, Mach-O, Windows PE/COFF, Wasm, XCOFF, and Unix archive. For writing files, it provides: * low level writers for ELF, PE, and COFF * higher level builder for ELF ([example](crates/rewrite/src)) * a unified API for writing relocatable object files (ELF, Mach-O, COFF, XCOFF) ([example](crates/examples/src/bin/simple_write.rs)) ## Example for unified read API ```rust use object::{Object, ObjectSection}; use std::error::Error; use std::fs; /// Reads a file and displays the name of each section. fn main() -> Result<(), Box> { let binary_data = fs::read("path/to/binary")?; let file = object::File::parse(&*binary_data)?; for section in file.sections() { println!("{}", section.name()?); } Ok(()) } ``` See [`crates/examples`](crates/examples) for more examples. ## Minimum Supported Rust Version (MSRV) Changes to MSRV are considered breaking changes. We are conservative about changing the MSRV, but sometimes are required to due to dependencies. The MSRV is 1.65.0. ## License Licensed under either of * Apache License, Version 2.0 ([`LICENSE-APACHE`](./LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) * MIT license ([`LICENSE-MIT`](./LICENSE-MIT) or https://opensource.org/licenses/MIT) at your option. ## Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. object-0.36.5/src/archive.rs000064400000000000000000000051551046102023000137510ustar 00000000000000//! Archive definitions. //! //! These definitions are independent of read/write support, although we do implement //! some traits useful for those. use crate::pod::Pod; /// File identification bytes stored at the beginning of the file. pub const MAGIC: [u8; 8] = *b"!\n"; /// File identification bytes at the beginning of AIX big archive. pub const AIX_BIG_MAGIC: [u8; 8] = *b"\n"; /// File identification bytes stored at the beginning of a thin archive. /// /// A thin archive only contains a symbol table and file names. pub const THIN_MAGIC: [u8; 8] = *b"!\n"; /// The terminator for each archive member header. pub const TERMINATOR: [u8; 2] = *b"`\n"; /// The header at the start of an archive member. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Header { /// The file name. pub name: [u8; 16], /// File modification timestamp in decimal. pub date: [u8; 12], /// User ID in decimal. pub uid: [u8; 6], /// Group ID in decimal. pub gid: [u8; 6], /// File mode in octal. pub mode: [u8; 8], /// File size in decimal. pub size: [u8; 10], /// Must be equal to `TERMINATOR`. pub terminator: [u8; 2], } /// The header at the start of an AIX big archive member, without name. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct AixHeader { /// File member size in decimal. pub size: [u8; 20], /// Next member offset in decimal. pub nxtmem: [u8; 20], /// Previous member offset in decimal. pub prvmem: [u8; 20], /// File member date in decimal. pub date: [u8; 12], /// File member user id in decimal. pub uid: [u8; 12], /// File member group id in decimal. pub gid: [u8; 12], /// File member mode in octal. pub mode: [u8; 12], /// File member name length in decimal. pub namlen: [u8; 4], } /// The AIX big archive's fixed length header at file beginning. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct AixFileHeader { /// Archive magic string. pub magic: [u8; 8], /// Offset of member table. pub memoff: [u8; 20], /// Offset of global symbol table. pub gstoff: [u8; 20], /// Offset of global symbol table for 64-bit objects. pub gst64off: [u8; 20], /// Offset of first member. pub fstmoff: [u8; 20], /// Offset of last member. pub lstmoff: [u8; 20], /// Offset of first member on free list. pub freeoff: [u8; 20], } /// Offset of a member in an AIX big archive. /// /// This is used in the member index. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct AixMemberOffset(pub [u8; 20]); unsafe_impl_pod!(Header, AixHeader, AixFileHeader, AixMemberOffset,); object-0.36.5/src/build/bytes.rs000064400000000000000000000071061046102023000145530ustar 00000000000000use alloc::borrow::Cow; use alloc::string::String; use alloc::vec::Vec; use core::fmt; /// A byte slice. /// /// Uses copy-on-write to avoid unnecessary allocations. The bytes can be /// accessed as a slice using the `Deref` trait, or as a mutable `Vec` using the /// `to_mut` method. /// /// Provides a `Debug` implementation that shows the first 8 bytes and the length. #[derive(Default, Clone, PartialEq, Eq)] pub struct Bytes<'a>(Cow<'a, [u8]>); impl<'a> Bytes<'a> { /// Acquire a mutable reference to the bytes. /// /// Clones the bytes if they are shared. pub fn to_mut(&mut self) -> &mut Vec { self.0.to_mut() } /// Get the bytes as a slice. pub fn as_slice(&self) -> &[u8] { self.0.as_ref() } } impl<'a> core::ops::Deref for Bytes<'a> { type Target = [u8]; fn deref(&self) -> &[u8] { self.0.deref() } } impl<'a> From<&'a [u8]> for Bytes<'a> { fn from(bytes: &'a [u8]) -> Self { Bytes(Cow::Borrowed(bytes)) } } impl<'a> From> for Bytes<'a> { fn from(bytes: Vec) -> Self { Bytes(Cow::Owned(bytes)) } } impl<'a> fmt::Debug for Bytes<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { debug_list_bytes(&self.0, f) } } // Only for Debug impl of `Bytes`. fn debug_list_bytes(bytes: &[u8], fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = fmt.debug_list(); list.entries(bytes.iter().take(8).copied().map(DebugByte)); if bytes.len() > 8 { list.entry(&DebugLen(bytes.len())); } list.finish() } struct DebugByte(u8); impl fmt::Debug for DebugByte { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!(fmt, "0x{:02x}", self.0) } } struct DebugLen(usize); impl fmt::Debug for DebugLen { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!(fmt, "...; {}", self.0) } } /// A byte slice that is a string of an unknown encoding. /// /// Uses copy-on-write to avoid unnecessary allocations. The bytes can be /// accessed as a slice using the `Deref` trait, or as a mutable `Vec` using the /// `to_mut` method. /// /// Provides a `Debug` implementation that interprets the bytes as UTF-8. #[derive(Default, Clone, PartialEq, Eq, Hash)] pub struct ByteString<'a>(Cow<'a, [u8]>); impl<'a> ByteString<'a> { /// Acquire a mutable reference to the bytes. /// /// Clones the bytes if they are shared. pub fn to_mut(&mut self) -> &mut Vec { self.0.to_mut() } /// Get the bytes as a slice. pub fn as_slice(&self) -> &[u8] { self.0.as_ref() } } impl<'a> core::borrow::Borrow<[u8]> for ByteString<'a> { fn borrow(&self) -> &[u8] { self.0.borrow() } } impl<'a> core::ops::Deref for ByteString<'a> { type Target = [u8]; fn deref(&self) -> &[u8] { self.0.deref() } } impl<'a> From<&'a [u8]> for ByteString<'a> { fn from(bytes: &'a [u8]) -> Self { ByteString(Cow::Borrowed(bytes)) } } impl<'a> From> for ByteString<'a> { fn from(bytes: Vec) -> Self { ByteString(Cow::Owned(bytes)) } } impl<'a> From<&'a str> for ByteString<'a> { fn from(s: &'a str) -> Self { ByteString(Cow::Borrowed(s.as_bytes())) } } impl<'a> fmt::Debug for ByteString<'a> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!(fmt, "\"{}\"", String::from_utf8_lossy(&self.0)) } } impl<'a> fmt::Display for ByteString<'a> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!(fmt, "{}", String::from_utf8_lossy(&self.0)) } } object-0.36.5/src/build/elf.rs000064400000000000000000003502301046102023000141720ustar 00000000000000//! This module provides a [`Builder`] for reading, modifying, and then writing ELF files. use alloc::vec::Vec; use core::convert::TryInto; use core::fmt; use core::marker::PhantomData; #[cfg(not(feature = "std"))] use hashbrown::HashMap; #[cfg(feature = "std")] use std::collections::HashMap; use crate::build::{ByteString, Bytes, Error, Id, IdPrivate, Item, Result, Table}; use crate::elf; use crate::read::elf::{Dyn, FileHeader, ProgramHeader, Rela, SectionHeader, Sym}; use crate::read::{self, FileKind, ReadRef}; use crate::write; use crate::Endianness; /// A builder for reading, modifying, and then writing ELF files. /// /// Public fields are available for modifying the values that will be written. /// Methods are available to add elements to tables, and elements can be deleted /// from tables by setting the `delete` field in the element. #[derive(Debug)] pub struct Builder<'data> { /// The endianness. /// /// Used to set the data encoding when writing the ELF file. pub endian: Endianness, /// Whether file is 64-bit. /// /// Use to set the file class when writing the ELF file. pub is_64: bool, /// The alignment of [`elf::PT_LOAD`] segments. /// /// This is an informational field and is not used when writing the ELF file. /// It can optionally be used when calling [`Segments::add_load_segment`]. /// /// It is determined heuristically when reading the ELF file. Currently, /// if all load segments have the same alignment, that alignment is used, /// otherwise it is set to 1. pub load_align: u64, /// The file header. pub header: Header, /// The segment table. pub segments: Segments<'data>, /// The section table. pub sections: Sections<'data>, /// The symbol table. pub symbols: Symbols<'data>, /// The dynamic symbol table. pub dynamic_symbols: DynamicSymbols<'data>, /// The base version for the GNU version definitions. /// /// This will be written as a version definition with index 1. pub version_base: Option>, /// The GNU version definitions and dependencies. pub versions: Versions<'data>, /// The filenames used in the GNU version definitions. pub version_files: VersionFiles<'data>, /// The bucket count parameter for the hash table. pub hash_bucket_count: u32, /// The bloom shift parameter for the GNU hash table. pub gnu_hash_bloom_shift: u32, /// The bloom count parameter for the GNU hash table. pub gnu_hash_bloom_count: u32, /// The bucket count parameter for the GNU hash table. pub gnu_hash_bucket_count: u32, marker: PhantomData<()>, } impl<'data> Builder<'data> { /// Create a new ELF builder. pub fn new(endian: Endianness, is_64: bool) -> Self { Self { endian, is_64, load_align: 1, header: Header::default(), segments: Segments::new(), sections: Sections::new(), symbols: Symbols::new(), dynamic_symbols: Symbols::new(), version_base: None, versions: Versions::new(), version_files: VersionFiles::new(), hash_bucket_count: 0, gnu_hash_bloom_shift: 0, gnu_hash_bloom_count: 0, gnu_hash_bucket_count: 0, marker: PhantomData, } } /// Read the ELF file from file data. pub fn read>(data: R) -> Result { match FileKind::parse(data)? { FileKind::Elf32 => Self::read32(data), FileKind::Elf64 => Self::read64(data), #[allow(unreachable_patterns)] _ => Err(Error::new("Not an ELF file")), } } /// Read a 32-bit ELF file from file data. pub fn read32>(data: R) -> Result { Self::read_file::, R>(data) } /// Read a 64-bit ELF file from file data. pub fn read64>(data: R) -> Result { Self::read_file::, R>(data) } fn read_file(data: R) -> Result where Elf: FileHeader, R: ReadRef<'data>, { let header = Elf::parse(data)?; let endian = header.endian()?; let is_mips64el = header.is_mips64el(endian); let section_strings_index = header.section_strings_index(endian, data)?; let segments = header.program_headers(endian, data)?; let sections = header.sections(endian, data)?; let symbols = sections.symbols(endian, data, elf::SHT_SYMTAB)?; let dynamic_symbols = sections.symbols(endian, data, elf::SHT_DYNSYM)?; let mut builder = Builder { endian, is_64: header.is_type_64(), load_align: 0, header: Header { os_abi: header.e_ident().os_abi, abi_version: header.e_ident().abi_version, e_type: header.e_type(endian), e_machine: header.e_machine(endian), e_entry: header.e_entry(endian).into(), e_flags: header.e_flags(endian), e_phoff: header.e_phoff(endian).into(), }, segments: Segments::new(), sections: Sections::new(), symbols: Symbols::new(), dynamic_symbols: Symbols::new(), version_base: None, versions: Versions::new(), version_files: VersionFiles::new(), hash_bucket_count: 0, gnu_hash_bloom_shift: 0, gnu_hash_bloom_count: 0, gnu_hash_bucket_count: 0, marker: PhantomData, }; for segment in segments { if segment.p_type(endian) == elf::PT_LOAD { let p_align = segment.p_align(endian).into(); if builder.load_align == 0 { builder.load_align = p_align; } else if builder.load_align != p_align { builder.load_align = 1; } } let id = builder.segments.next_id(); builder.segments.push(Segment { id, delete: false, p_type: segment.p_type(endian), p_flags: segment.p_flags(endian), p_offset: segment.p_offset(endian).into(), p_vaddr: segment.p_vaddr(endian).into(), p_paddr: segment.p_paddr(endian).into(), p_filesz: segment.p_filesz(endian).into(), p_memsz: segment.p_memsz(endian).into(), p_align: segment.p_align(endian).into(), sections: Vec::new(), marker: PhantomData, }); } if builder.load_align == 0 { builder.load_align = 1; } for (index, section) in sections.enumerate().skip(1) { let id = SectionId(index.0 - 1); let relocations = if let Some((rels, link)) = section.rel(endian, data)? { Self::read_relocations( index, endian, is_mips64el, section, rels, link, &symbols, &dynamic_symbols, )? } else if let Some((rels, link)) = section.rela(endian, data)? { Self::read_relocations( index, endian, is_mips64el, section, rels, link, &symbols, &dynamic_symbols, )? } else { SectionData::Data(Bytes::default()) }; if let Some(hash) = section.hash_header(endian, data)? { builder.hash_bucket_count = hash.bucket_count.get(endian); } if let Some(hash) = section.gnu_hash_header(endian, data)? { builder.gnu_hash_bloom_shift = hash.bloom_shift.get(endian); builder.gnu_hash_bloom_count = hash.bloom_count.get(endian); builder.gnu_hash_bucket_count = hash.bucket_count.get(endian); } let data = match section.sh_type(endian) { elf::SHT_NOBITS => SectionData::UninitializedData(section.sh_size(endian).into()), elf::SHT_PROGBITS | elf::SHT_INIT_ARRAY | elf::SHT_FINI_ARRAY | elf::SHT_PREINIT_ARRAY => SectionData::Data(section.data(endian, data)?.into()), elf::SHT_REL | elf::SHT_RELA => relocations, elf::SHT_SYMTAB => { if index == symbols.section() { SectionData::Symbol } else { return Err(Error(format!( "Unsupported SHT_SYMTAB section at index {}", index ))); } } elf::SHT_SYMTAB_SHNDX => { if index == symbols.shndx_section() { SectionData::SymbolSectionIndex } else { return Err(Error(format!( "Unsupported SHT_SYMTAB_SHNDX section at index {}", index ))); } } elf::SHT_DYNSYM => { if index == dynamic_symbols.section() { SectionData::DynamicSymbol } else { return Err(Error(format!( "Unsupported SHT_DYNSYM section at index {}", index ))); } } elf::SHT_STRTAB => { if index == symbols.string_section() { SectionData::String } else if index == dynamic_symbols.string_section() { SectionData::DynamicString } else if index == section_strings_index { SectionData::SectionString } else { return Err(Error(format!( "Unsupported SHT_STRTAB section at index {}", index ))); } } elf::SHT_NOTE => SectionData::Note(section.data(endian, data)?.into()), elf::SHT_DYNAMIC => { let (dyns, link) = section.dynamic(endian, data)?.unwrap(); let dynamic_strings = sections.strings(endian, data, link)?; Self::read_dynamics::(endian, dyns, dynamic_strings)? } elf::SHT_GNU_ATTRIBUTES => { let attributes = section.attributes(endian, data)?; Self::read_attributes(index, attributes, sections.len(), symbols.len())? } elf::SHT_HASH => SectionData::Hash, elf::SHT_GNU_HASH => SectionData::GnuHash, elf::SHT_GNU_VERSYM => SectionData::GnuVersym, elf::SHT_GNU_VERDEF => SectionData::GnuVerdef, elf::SHT_GNU_VERNEED => SectionData::GnuVerneed, other => match (builder.header.e_machine, other) { (elf::EM_ARM, elf::SHT_ARM_ATTRIBUTES) | (elf::EM_AARCH64, elf::SHT_AARCH64_ATTRIBUTES) | (elf::EM_CSKY, elf::SHT_CSKY_ATTRIBUTES) | (elf::EM_RISCV, elf::SHT_RISCV_ATTRIBUTES) => { let attributes = section.attributes(endian, data)?; Self::read_attributes(index, attributes, sections.len(), symbols.len())? } // Some section types that we can't parse but that are safe to copy. // Lots of types missing, add as needed. We can't default to copying // everything because some types are not safe to copy. (elf::EM_ARM, elf::SHT_ARM_EXIDX) | (elf::EM_IA_64, elf::SHT_IA_64_UNWIND) | (elf::EM_MIPS, elf::SHT_MIPS_REGINFO) | (elf::EM_MIPS, elf::SHT_MIPS_DWARF) | (elf::EM_X86_64, elf::SHT_X86_64_UNWIND) => { SectionData::Data(section.data(endian, data)?.into()) } _ => return Err(Error(format!("Unsupported section type {:x}", other))), }, }; let sh_flags = section.sh_flags(endian).into(); let sh_link = section.sh_link(endian); let sh_link_section = if sh_link == 0 { None } else { if sh_link as usize >= sections.len() { return Err(Error(format!( "Invalid sh_link {} in section at index {}", sh_link, index ))); } Some(SectionId(sh_link as usize - 1)) }; let sh_info = section.sh_info(endian); let sh_info_section = if sh_info == 0 || sh_flags & u64::from(elf::SHF_INFO_LINK) == 0 { None } else { if sh_info as usize >= sections.len() { return Err(Error(format!( "Invalid sh_info link {} in section at index {}", sh_info, index ))); } Some(SectionId(sh_info as usize - 1)) }; let sh_flags = section.sh_flags(endian).into(); let sh_addr = section.sh_addr(endian).into(); if sh_flags & u64::from(elf::SHF_ALLOC) != 0 { for segment in &mut builder.segments { if segment.contains_address(sh_addr) { segment.sections.push(id); } } } builder.sections.push(Section { id, delete: false, name: sections.section_name(endian, section)?.into(), sh_type: section.sh_type(endian), sh_flags, sh_addr, sh_offset: section.sh_offset(endian).into(), sh_size: section.sh_size(endian).into(), sh_link_section, sh_info, sh_info_section, sh_addralign: section.sh_addralign(endian).into(), sh_entsize: section.sh_entsize(endian).into(), data, }); } Self::read_symbols( endian, &symbols, &mut builder.symbols, builder.sections.len(), )?; Self::read_symbols( endian, &dynamic_symbols, &mut builder.dynamic_symbols, builder.sections.len(), )?; builder.read_gnu_versions(endian, data, §ions, &dynamic_symbols)?; Ok(builder) } #[allow(clippy::too_many_arguments)] fn read_relocations( index: read::SectionIndex, endian: Elf::Endian, is_mips64el: bool, section: &'data Elf::SectionHeader, rels: &'data [Rel], link: read::SectionIndex, symbols: &read::elf::SymbolTable<'data, Elf, R>, dynamic_symbols: &read::elf::SymbolTable<'data, Elf, R>, ) -> Result> where Elf: FileHeader, Rel: Copy + Into, R: ReadRef<'data>, { if link == dynamic_symbols.section() { Self::read_relocations_impl::( index, endian, is_mips64el, rels, dynamic_symbols.len(), ) .map(SectionData::DynamicRelocation) } else if link.0 == 0 || section.sh_flags(endian).into() & u64::from(elf::SHF_ALLOC) != 0 { // If there's no link, then none of the relocations may reference symbols. // Assume that these are dynamic relocations, but don't use the dynamic // symbol table when parsing. // // Additionally, sometimes there is an allocated section that links to // the static symbol table. We don't currently support this case in general, // but if none of the relocation entries reference a symbol then it is // safe to treat it as a dynamic relocation section. // // For both of these cases, if there is a reference to a symbol then // an error will be returned when parsing the relocations. Self::read_relocations_impl::(index, endian, is_mips64el, rels, 0) .map(SectionData::DynamicRelocation) } else if link == symbols.section() { Self::read_relocations_impl::( index, endian, is_mips64el, rels, symbols.len(), ) .map(SectionData::Relocation) } else { return Err(Error(format!( "Invalid sh_link {} in relocation section at index {}", link.0, index, ))); } } fn read_relocations_impl( index: read::SectionIndex, endian: Elf::Endian, is_mips64el: bool, rels: &'data [Rel], symbols_len: usize, ) -> Result>> where Elf: FileHeader, Rel: Copy + Into, { let mut relocations = Vec::new(); for rel in rels { let rel = (*rel).into(); let symbol = if let Some(symbol) = rel.symbol(endian, is_mips64el) { if symbol.0 >= symbols_len { return Err(Error(format!( "Invalid symbol index {} in relocation section at index {}", symbol, index, ))); } Some(SymbolId(symbol.0 - 1)) } else { None }; relocations.push(Relocation { r_offset: rel.r_offset(endian).into(), symbol, r_type: rel.r_type(endian, is_mips64el), r_addend: rel.r_addend(endian).into(), }); } Ok(relocations) } fn read_dynamics( endian: Elf::Endian, dyns: &'data [Elf::Dyn], strings: read::StringTable<'data, R>, ) -> Result> where Elf: FileHeader, R: ReadRef<'data>, { let mut dynamics = Vec::with_capacity(dyns.len()); for d in dyns { let tag = d.d_tag(endian).into().try_into().map_err(|_| { Error(format!( "Unsupported dynamic tag 0x{:x}", d.d_tag(endian).into() )) })?; if tag == elf::DT_NULL { break; } let val = d.d_val(endian).into(); dynamics.push(if d.is_string(endian) { let val = strings .get(val.try_into().map_err(|_| { Error(format!("Unsupported dynamic string 0x{:x}", val)) })?) .map_err(|_| Error(format!("Invalid dynamic string 0x{:x}", val)))?; Dynamic::String { tag, val: val.into(), } } else { match tag { elf::DT_SYMTAB | elf::DT_STRTAB | elf::DT_STRSZ | elf::DT_HASH | elf::DT_GNU_HASH | elf::DT_VERSYM | elf::DT_VERDEF | elf::DT_VERDEFNUM | elf::DT_VERNEED | elf::DT_VERNEEDNUM => Dynamic::Auto { tag }, _ => Dynamic::Integer { tag, val }, } }); } Ok(SectionData::Dynamic(dynamics)) } fn read_symbols( endian: Elf::Endian, symbols: &read::elf::SymbolTable<'data, Elf, R>, builder_symbols: &mut Symbols<'data, DYNAMIC>, sections_len: usize, ) -> Result<()> where Elf: FileHeader, R: ReadRef<'data>, { for (index, symbol) in symbols.enumerate().skip(1) { let id = SymbolId(index.0 - 1); let section = if let Some(section_index) = symbols.symbol_section(endian, symbol, index)? { let section_id = section_index.0.wrapping_sub(1); if section_id >= sections_len { return Err(Error::new("Invalid symbol section index")); } Some(SectionId(section_id)) } else { None }; builder_symbols.push(Symbol { id, delete: false, name: symbols.symbol_name(endian, symbol)?.into(), section, st_info: symbol.st_info(), st_other: symbol.st_other(), st_shndx: symbol.st_shndx(endian), st_value: symbol.st_value(endian).into(), st_size: symbol.st_size(endian).into(), version: VersionId::local(), version_hidden: false, }); } Ok(()) } fn read_attributes( index: read::SectionIndex, attributes: read::elf::AttributesSection<'data, Elf>, sections_len: usize, symbols_len: usize, ) -> Result> where Elf: FileHeader, { let mut builder_attributes = AttributesSection::new(); let mut subsections = attributes.subsections()?; while let Some(subsection) = subsections.next()? { let mut builder_subsection = AttributesSubsection::new(subsection.vendor().into()); let mut subsubsections = subsection.subsubsections(); while let Some(subsubsection) = subsubsections.next()? { let tag = match subsubsection.tag() { elf::Tag_File => AttributeTag::File, elf::Tag_Section => { let mut tag_sections = Vec::new(); let mut indices = subsubsection.indices(); while let Some(index) = indices.next()? { let index = index as usize; if index >= sections_len { return Err(Error(format!( "Invalid section index {} in attribute", index ))); } tag_sections.push(SectionId(index - 1)); } AttributeTag::Section(tag_sections) } elf::Tag_Symbol => { let mut tag_symbols = Vec::new(); let mut indices = subsubsection.indices(); while let Some(index) = indices.next()? { let index = index as usize; if index >= symbols_len { return Err(Error(format!( "Invalid symbol index {} in attribute", index ))); } tag_symbols.push(SymbolId(index - 1)); } AttributeTag::Symbol(tag_symbols) } tag => { return Err(Error(format!( "Unsupported attribute tag 0x{:x} in section at index {}", tag, index, ))) } }; let data = subsubsection.attributes_data().into(); builder_subsection .subsubsections .push(AttributesSubsubsection { tag, data }); } builder_attributes.subsections.push(builder_subsection); } Ok(SectionData::Attributes(builder_attributes)) } fn read_gnu_versions( &mut self, endian: Elf::Endian, data: R, sections: &read::elf::SectionTable<'data, Elf, R>, dynamic_symbols: &read::elf::SymbolTable<'data, Elf, R>, ) -> Result<()> where Elf: FileHeader, R: ReadRef<'data>, { let strings = dynamic_symbols.strings(); let mut ids = HashMap::new(); ids.insert(0, VersionId::local()); ids.insert(1, VersionId::global()); if let Some((mut verdefs, link)) = sections.gnu_verdef(endian, data)? { if link != dynamic_symbols.string_section() { return Err(Error::new("Invalid SHT_GNU_VERDEF section")); } while let Some((verdef, mut verdauxs)) = verdefs.next()? { let flags = verdef.vd_flags.get(endian); if flags & elf::VER_FLG_BASE != 0 { if flags != elf::VER_FLG_BASE || verdef.vd_ndx.get(endian) != 1 || verdef.vd_cnt.get(endian) != 1 { return Err(Error::new("Unsupported VER_FLG_BASE in SHT_GNU_VERDEF")); } if self.version_base.is_some() { return Err(Error::new("Duplicate VER_FLG_BASE in SHT_GNU_VERDEF")); } let verdaux = verdauxs.next()?.ok_or_else(|| { Error::new("Missing name for VER_FLG_BASE in SHT_GNU_VERDEF") })?; self.version_base = Some(verdaux.name(endian, strings)?.into()); continue; } let index = verdef.vd_ndx.get(endian) & elf::VERSYM_VERSION; let id = self.versions.next_id(); if ids.insert(index, id).is_some() { return Err(Error(format!("Duplicate SHT_GNU_VERDEF index {}", index))); } let mut names = Vec::new(); while let Some(verdaux) = verdauxs.next()? { names.push(verdaux.name(endian, strings)?.into()); } let data = VersionData::Def(VersionDef { flags, names }); self.versions.push(Version { id, delete: false, data, }); } } if let Some((mut verneeds, link)) = sections.gnu_verneed(endian, data)? { if link != dynamic_symbols.string_section() { return Err(Error::new("Invalid SHT_GNU_VERNEED section")); } while let Some((verneed, mut vernauxs)) = verneeds.next()? { let file = VersionFileId(self.version_files.len()); self.version_files.push(VersionFile { id: file, delete: false, name: verneed.file(endian, strings)?.into(), }); while let Some(vernaux) = vernauxs.next()? { let index = vernaux.vna_other.get(endian) & elf::VERSYM_VERSION; let id = self.versions.next_id(); if ids.insert(index, id).is_some() { return Err(Error(format!("Duplicate SHT_GNU_VERNEED index {}", index))); } let data = VersionData::Need(VersionNeed { flags: vernaux.vna_flags.get(endian), name: vernaux.name(endian, strings)?.into(), file, }); self.versions.push(Version { id, delete: false, data, }); } } } if let Some((versyms, link)) = sections.gnu_versym(endian, data)? { if versyms.len() != dynamic_symbols.len() || link != dynamic_symbols.section() { return Err(Error::new("Invalid SHT_GNU_VERSYM section")); } for (id, versym) in versyms.iter().skip(1).enumerate() { let index = versym.0.get(endian); let symbol = self.dynamic_symbols.get_mut(SymbolId(id)); symbol.version = *ids .get(&(index & elf::VERSYM_VERSION)) .ok_or_else(|| Error(format!("Invalid SHT_GNU_VERSYM index {:x}", index)))?; symbol.version_hidden = index & elf::VERSYM_HIDDEN != 0; } } Ok(()) } /// Write the ELF file to the buffer. pub fn write(mut self, buffer: &mut dyn write::WritableBuffer) -> Result<()> { struct SectionOut { id: SectionId, name: Option, offset: usize, attributes: Vec, } struct SymbolOut { id: SymbolId, name: Option, } struct DynamicSymbolOut { id: DynamicSymbolId, name: Option, hash: Option, gnu_hash: Option, } #[derive(Default, Clone)] struct VersionFileOut { versions: Vec, } // TODO: require the caller to do this? self.delete_orphans(); self.delete_unused_versions(); let mut writer = write::elf::Writer::new(self.endian, self.is_64, buffer); // Find metadata sections, and assign section indices. let mut shstrtab_id = None; let mut symtab_id = None; let mut symtab_shndx_id = None; let mut strtab_id = None; let mut dynsym_id = None; let mut dynstr_id = None; let mut hash_id = None; let mut gnu_hash_id = None; let mut gnu_versym_id = None; let mut gnu_verdef_id = None; let mut gnu_verneed_id = None; let mut out_sections = Vec::with_capacity(self.sections.len()); let mut out_sections_index = vec![None; self.sections.len()]; if !self.sections.is_empty() { writer.reserve_null_section_index(); } for section in &self.sections { let index = match §ion.data { SectionData::Data(_) | SectionData::UninitializedData(_) | SectionData::Relocation(_) | SectionData::DynamicRelocation(_) | SectionData::Note(_) | SectionData::Dynamic(_) | SectionData::Attributes(_) => writer.reserve_section_index(), SectionData::SectionString => { if shstrtab_id.is_some() { return Err(Error::new("Multiple .shstrtab sections")); } shstrtab_id = Some(section.id); writer.reserve_shstrtab_section_index_with_name(§ion.name) } SectionData::Symbol => { if symtab_id.is_some() { return Err(Error::new("Multiple .symtab sections")); } symtab_id = Some(section.id); writer.reserve_symtab_section_index_with_name(§ion.name) } SectionData::SymbolSectionIndex => { if symtab_shndx_id.is_some() { return Err(Error::new("Multiple .symtab_shndx sections")); } symtab_shndx_id = Some(section.id); writer.reserve_symtab_shndx_section_index_with_name(§ion.name) } SectionData::String => { if strtab_id.is_some() { return Err(Error::new("Multiple .strtab sections")); } strtab_id = Some(section.id); writer.reserve_strtab_section_index_with_name(§ion.name) } SectionData::DynamicSymbol => { if dynsym_id.is_some() { return Err(Error::new("Multiple .dynsym sections")); } dynsym_id = Some(section.id); writer.reserve_dynsym_section_index_with_name(§ion.name) } SectionData::DynamicString => { if dynstr_id.is_some() { return Err(Error::new("Multiple .dynstr sections")); } dynstr_id = Some(section.id); writer.reserve_dynstr_section_index_with_name(§ion.name) } SectionData::Hash => { if hash_id.is_some() { return Err(Error::new("Multiple .hash sections")); } hash_id = Some(section.id); writer.reserve_hash_section_index_with_name(§ion.name) } SectionData::GnuHash => { if gnu_hash_id.is_some() { return Err(Error::new("Multiple .gnu.hash sections")); } gnu_hash_id = Some(section.id); writer.reserve_gnu_hash_section_index_with_name(§ion.name) } SectionData::GnuVersym => { if gnu_versym_id.is_some() { return Err(Error::new("Multiple .gnu.version sections")); } gnu_versym_id = Some(section.id); writer.reserve_gnu_versym_section_index_with_name(§ion.name) } SectionData::GnuVerdef => { if gnu_verdef_id.is_some() { return Err(Error::new("Multiple .gnu.version_d sections")); } gnu_verdef_id = Some(section.id); writer.reserve_gnu_verdef_section_index_with_name(§ion.name) } SectionData::GnuVerneed => { if gnu_verneed_id.is_some() { return Err(Error::new("Multiple .gnu.version_r sections")); } gnu_verneed_id = Some(section.id); writer.reserve_gnu_verneed_section_index_with_name(§ion.name) } }; out_sections_index[section.id.0] = Some(index); let name = if section.name.is_empty() { None } else { Some(writer.add_section_name(§ion.name)) }; out_sections.push(SectionOut { id: section.id, name, offset: 0, attributes: Vec::new(), }); } // Assign dynamic strings. for section in &self.sections { if let SectionData::Dynamic(dynamics) = §ion.data { for dynamic in dynamics { if let Dynamic::String { val, .. } = dynamic { writer.add_dynamic_string(val); } } } } // Assign dynamic symbol indices. let mut out_dynsyms = Vec::with_capacity(self.dynamic_symbols.len()); // Local symbols must come before global. let local_symbols = self .dynamic_symbols .into_iter() .filter(|symbol| symbol.st_bind() == elf::STB_LOCAL); let global_symbols = self .dynamic_symbols .into_iter() .filter(|symbol| symbol.st_bind() != elf::STB_LOCAL); for symbol in local_symbols.chain(global_symbols) { let mut name = None; let mut hash = None; let mut gnu_hash = None; if !symbol.name.is_empty() { name = Some(writer.add_dynamic_string(&symbol.name)); if hash_id.is_some() { hash = Some(elf::hash(&symbol.name)); } if gnu_hash_id.is_some() && (symbol.section.is_some() || symbol.st_shndx != elf::SHN_UNDEF) { gnu_hash = Some(elf::gnu_hash(&symbol.name)); } } out_dynsyms.push(DynamicSymbolOut { id: symbol.id, name, hash, gnu_hash, }); } let num_local_dynamic = out_dynsyms .iter() .take_while(|sym| self.dynamic_symbols.get(sym.id).st_bind() == elf::STB_LOCAL) .count(); // We must sort for GNU hash before allocating symbol indices. let mut gnu_hash_symbol_count = 0; if gnu_hash_id.is_some() { if self.gnu_hash_bucket_count == 0 { return Err(Error::new(".gnu.hash bucket count is zero")); } // TODO: recalculate bucket_count? out_dynsyms[num_local_dynamic..].sort_by_key(|sym| match sym.gnu_hash { None => (0, 0), Some(hash) => (1, hash % self.gnu_hash_bucket_count), }); gnu_hash_symbol_count = out_dynsyms .iter() .skip(num_local_dynamic) .skip_while(|sym| sym.gnu_hash.is_none()) .count() as u32; } let mut out_dynsyms_index = vec![None; self.dynamic_symbols.len()]; if dynsym_id.is_some() { writer.reserve_null_dynamic_symbol_index(); } for out_dynsym in &mut out_dynsyms { out_dynsyms_index[out_dynsym.id.0] = Some(writer.reserve_dynamic_symbol_index()); } // Hash parameters. let hash_index_base = 1; // Null symbol. let hash_chain_count = hash_index_base + out_dynsyms.len() as u32; // GNU hash parameters. let gnu_hash_index_base = if gnu_hash_symbol_count == 0 { 0 } else { out_dynsyms.len() as u32 - gnu_hash_symbol_count }; let gnu_hash_symbol_base = gnu_hash_index_base + 1; // Null symbol. // Assign symbol indices. let mut out_syms = Vec::with_capacity(self.symbols.len()); // Local symbols must come before global. let local_symbols = self .symbols .into_iter() .filter(|symbol| symbol.st_bind() == elf::STB_LOCAL); let global_symbols = self .symbols .into_iter() .filter(|symbol| symbol.st_bind() != elf::STB_LOCAL); for symbol in local_symbols.chain(global_symbols) { let name = if symbol.name.is_empty() { None } else { Some(writer.add_string(&symbol.name)) }; out_syms.push(SymbolOut { id: symbol.id, name, }); } let num_local = out_syms .iter() .take_while(|sym| self.symbols.get(sym.id).st_bind() == elf::STB_LOCAL) .count(); let mut out_syms_index = vec![None; self.symbols.len()]; if symtab_id.is_some() { writer.reserve_null_symbol_index(); } for out_sym in out_syms.iter_mut() { out_syms_index[out_sym.id.0] = Some(writer.reserve_symbol_index(None)); } // Count the versions and add version strings. let mut verdef_count = 0; let mut verdaux_count = 0; let mut verdef_shared_base = false; let mut verneed_count = 0; let mut vernaux_count = 0; let mut out_version_files = vec![VersionFileOut::default(); self.version_files.len()]; if let Some(version_base) = &self.version_base { verdef_count += 1; verdaux_count += 1; writer.add_dynamic_string(version_base); } for version in &self.versions { match &version.data { VersionData::Def(def) => { if def.is_shared(verdef_count, self.version_base.as_ref()) { verdef_shared_base = true; } else { verdaux_count += def.names.len(); for name in &def.names { writer.add_dynamic_string(name); } } verdef_count += 1; } VersionData::Need(need) => { vernaux_count += 1; writer.add_dynamic_string(&need.name); out_version_files[need.file.0].versions.push(version.id); } } } for file in &self.version_files { verneed_count += 1; writer.add_dynamic_string(&file.name); } // Build the attributes sections. for out_section in &mut out_sections { let SectionData::Attributes(attributes) = &self.sections.get(out_section.id).data else { continue; }; if attributes.subsections.is_empty() { continue; } let mut writer = writer.attributes_writer(); for subsection in &attributes.subsections { writer.start_subsection(&subsection.vendor); for subsubsection in &subsection.subsubsections { writer.start_subsubsection(subsubsection.tag.tag()); match &subsubsection.tag { AttributeTag::File => {} AttributeTag::Section(sections) => { for id in sections { if let Some(index) = out_sections_index[id.0] { writer.write_subsubsection_index(index.0); } } writer.write_subsubsection_index(0); } AttributeTag::Symbol(symbols) => { for id in symbols { if let Some(index) = out_syms_index[id.0] { writer.write_subsubsection_index(index.0); } } writer.write_subsubsection_index(0); } } writer.write_subsubsection_attributes(&subsubsection.data); writer.end_subsubsection(); } writer.end_subsection(); } out_section.attributes = writer.data(); } // TODO: support section headers in strtab if shstrtab_id.is_none() && !out_sections.is_empty() { return Err(Error::new(".shstrtab section is needed but not present")); } if symtab_id.is_none() && !out_syms.is_empty() { return Err(Error::new(".symtab section is needed but not present")); } if symtab_shndx_id.is_none() && writer.symtab_shndx_needed() { return Err(Error::new( ".symtab.shndx section is needed but not present", )); } else if symtab_shndx_id.is_some() { writer.require_symtab_shndx(); } if strtab_id.is_none() && writer.strtab_needed() { return Err(Error::new(".strtab section is needed but not present")); } else if strtab_id.is_some() { writer.require_strtab(); } if dynsym_id.is_none() && !out_dynsyms.is_empty() { return Err(Error::new(".dynsym section is needed but not present")); } if dynstr_id.is_none() && writer.dynstr_needed() { return Err(Error::new(".dynstr section is needed but not present")); } else if dynstr_id.is_some() { writer.require_dynstr(); } if gnu_verdef_id.is_none() && verdef_count > 0 { return Err(Error::new( ".gnu.version_d section is needed but not present", )); } if gnu_verneed_id.is_none() && verneed_count > 0 { return Err(Error::new( ".gnu.version_r section is needed but not present", )); } // Start reserving file ranges. writer.reserve_file_header(); let mut dynsym_addr = None; let mut dynstr_addr = None; let mut hash_addr = None; let mut gnu_hash_addr = None; let mut versym_addr = None; let mut verdef_addr = None; let mut verneed_addr = None; if !self.segments.is_empty() { // TODO: support program headers in other locations. if self.header.e_phoff != writer.reserved_len() as u64 { return Err(Error(format!( "Unsupported e_phoff value 0x{:x}", self.header.e_phoff ))); } writer.reserve_program_headers(self.segments.count() as u32); } let mut alloc_sections = Vec::new(); if !self.segments.is_empty() { // Reserve alloc sections at original offsets. alloc_sections = out_sections .iter() .enumerate() .filter_map(|(index, out_section)| { let section = self.sections.get(out_section.id); if section.is_alloc() { Some(index) } else { None } }) .collect(); // The data for alloc sections may need to be written in a different order // from their section headers. alloc_sections.sort_by_key(|index| { let section = &self.sections.get(out_sections[*index].id); // Empty sections need to come before other sections at the same offset. (section.sh_offset, section.sh_size) }); for index in &alloc_sections { let out_section = &mut out_sections[*index]; let section = &self.sections.get(out_section.id); if section.sh_type == elf::SHT_NOBITS { // sh_offset is meaningless for SHT_NOBITS, so preserve the input // value without checking it. out_section.offset = section.sh_offset as usize; continue; } if section.sh_offset < writer.reserved_len() as u64 { return Err(Error(format!( "Unsupported sh_offset value 0x{:x} for section '{}', expected at least 0x{:x}", section.sh_offset, section.name, writer.reserved_len(), ))); } // The input sh_offset needs to be preserved so that offsets in program // headers are correct. writer.reserve_until(section.sh_offset as usize); out_section.offset = match §ion.data { SectionData::Data(data) => { writer.reserve(data.len(), section.sh_addralign as usize) } SectionData::DynamicRelocation(relocations) => writer .reserve_relocations(relocations.len(), section.sh_type == elf::SHT_RELA), SectionData::Note(data) => { writer.reserve(data.len(), section.sh_addralign as usize) } SectionData::Dynamic(dynamics) => writer.reserve_dynamics(1 + dynamics.len()), SectionData::DynamicSymbol => { dynsym_addr = Some(section.sh_addr); writer.reserve_dynsym() } SectionData::DynamicString => { dynstr_addr = Some(section.sh_addr); writer.reserve_dynstr() } SectionData::Hash => { hash_addr = Some(section.sh_addr); writer.reserve_hash(self.hash_bucket_count, hash_chain_count) } SectionData::GnuHash => { gnu_hash_addr = Some(section.sh_addr); writer.reserve_gnu_hash( self.gnu_hash_bloom_count, self.gnu_hash_bucket_count, gnu_hash_symbol_count, ) } SectionData::GnuVersym => { versym_addr = Some(section.sh_addr); writer.reserve_gnu_versym() } SectionData::GnuVerdef => { verdef_addr = Some(section.sh_addr); writer.reserve_gnu_verdef(verdef_count, verdaux_count) } SectionData::GnuVerneed => { verneed_addr = Some(section.sh_addr); writer.reserve_gnu_verneed(verneed_count, vernaux_count) } _ => { return Err(Error(format!( "Unsupported alloc section type {:x} for section '{}'", section.sh_type, section.name, ))); } }; if out_section.offset as u64 != section.sh_offset { return Err(Error(format!( "Unaligned sh_offset value 0x{:x} for section '{}', expected 0x{:x}", section.sh_offset, section.name, out_section.offset, ))); } } } // Reserve non-alloc sections at any offset. for out_section in &mut out_sections { let section = self.sections.get(out_section.id); if !self.segments.is_empty() && section.is_alloc() { continue; } out_section.offset = match §ion.data { SectionData::Data(data) => { writer.reserve(data.len(), section.sh_addralign as usize) } SectionData::UninitializedData(_) => writer.reserved_len(), SectionData::Note(data) => { writer.reserve(data.len(), section.sh_addralign as usize) } SectionData::Attributes(_) => { writer.reserve(out_section.attributes.len(), section.sh_addralign as usize) } // These are handled elsewhere. SectionData::Relocation(_) | SectionData::SectionString | SectionData::Symbol | SectionData::SymbolSectionIndex | SectionData::String => { continue; } _ => { return Err(Error(format!( "Unsupported non-alloc section type {:x}", section.sh_type ))); } }; } writer.reserve_symtab(); writer.reserve_symtab_shndx(); writer.reserve_strtab(); // Reserve non-alloc relocations. for out_section in &mut out_sections { let section = self.sections.get(out_section.id); if !self.segments.is_empty() && section.is_alloc() { continue; } let SectionData::Relocation(relocations) = §ion.data else { continue; }; out_section.offset = writer.reserve_relocations(relocations.len(), section.sh_type == elf::SHT_RELA); } writer.reserve_shstrtab(); writer.reserve_section_headers(); // Start writing. writer.write_file_header(&write::elf::FileHeader { os_abi: self.header.os_abi, abi_version: self.header.abi_version, e_type: self.header.e_type, e_machine: self.header.e_machine, e_entry: self.header.e_entry, e_flags: self.header.e_flags, })?; if !self.segments.is_empty() { writer.write_align_program_headers(); for segment in &self.segments { writer.write_program_header(&write::elf::ProgramHeader { p_type: segment.p_type, p_flags: segment.p_flags, p_offset: segment.p_offset, p_vaddr: segment.p_vaddr, p_paddr: segment.p_paddr, p_filesz: segment.p_filesz, p_memsz: segment.p_memsz, p_align: segment.p_align, }); } } // Write alloc sections. if !self.segments.is_empty() { for index in &alloc_sections { let out_section = &mut out_sections[*index]; let section = self.sections.get(out_section.id); if section.sh_type == elf::SHT_NOBITS { continue; } writer.pad_until(out_section.offset); match §ion.data { SectionData::Data(data) => { writer.write(data); } SectionData::DynamicRelocation(relocations) => { for rel in relocations { let r_sym = if let Some(symbol) = rel.symbol { out_dynsyms_index[symbol.0].unwrap().0 } else { 0 }; writer.write_relocation( section.sh_type == elf::SHT_RELA, &write::elf::Rel { r_offset: rel.r_offset, r_sym, r_type: rel.r_type, r_addend: rel.r_addend, }, ); } } SectionData::Note(data) => { writer.write(data); } SectionData::Dynamic(dynamics) => { for d in dynamics { match *d { Dynamic::Auto { tag } => { // TODO: support more values let val = match tag { elf::DT_SYMTAB => dynsym_addr.ok_or(Error::new( "Missing .dynsym section for DT_SYMTAB", ))?, elf::DT_STRTAB => dynstr_addr.ok_or(Error::new( "Missing .dynstr section for DT_STRTAB", ))?, elf::DT_STRSZ => writer.dynstr_len() as u64, elf::DT_HASH => hash_addr.ok_or(Error::new( "Missing .hash section for DT_HASH", ))?, elf::DT_GNU_HASH => gnu_hash_addr.ok_or(Error::new( "Missing .gnu.hash section for DT_GNU_HASH", ))?, elf::DT_VERSYM => versym_addr.ok_or(Error::new( "Missing .gnu.version section for DT_VERSYM", ))?, elf::DT_VERDEF => verdef_addr.ok_or(Error::new( "Missing .gnu.version_d section for DT_VERDEF", ))?, elf::DT_VERDEFNUM => verdef_count as u64, elf::DT_VERNEED => verneed_addr.ok_or(Error::new( "Missing .gnu.version_r section for DT_VERNEED", ))?, elf::DT_VERNEEDNUM => verneed_count as u64, _ => { return Err(Error(format!( "Cannot generate value for dynamic tag 0x{:x}", tag ))) } }; writer.write_dynamic(tag, val); } Dynamic::Integer { tag, val } => { writer.write_dynamic(tag, val); } Dynamic::String { tag, ref val } => { let val = writer.get_dynamic_string(val); writer.write_dynamic_string(tag, val); } } } writer.write_dynamic(elf::DT_NULL, 0); } SectionData::DynamicSymbol => { writer.write_null_dynamic_symbol(); for out_dynsym in &out_dynsyms { let symbol = self.dynamic_symbols.get(out_dynsym.id); let section = symbol.section.map(|id| out_sections_index[id.0].unwrap()); writer.write_dynamic_symbol(&write::elf::Sym { name: out_dynsym.name, section, st_info: symbol.st_info, st_other: symbol.st_other, st_shndx: symbol.st_shndx, st_value: symbol.st_value, st_size: symbol.st_size, }); } } SectionData::DynamicString => { writer.write_dynstr(); } SectionData::Hash => { if self.hash_bucket_count == 0 { return Err(Error::new(".hash bucket count is zero")); } writer.write_hash(self.hash_bucket_count, hash_chain_count, |index| { out_dynsyms .get(index.checked_sub(hash_index_base)? as usize)? .hash }); } SectionData::GnuHash => { if self.gnu_hash_bucket_count == 0 { return Err(Error::new(".gnu.hash bucket count is zero")); } writer.write_gnu_hash( gnu_hash_symbol_base, self.gnu_hash_bloom_shift, self.gnu_hash_bloom_count, self.gnu_hash_bucket_count, gnu_hash_symbol_count, |index| { out_dynsyms[(gnu_hash_index_base + index) as usize] .gnu_hash .unwrap() }, ); } SectionData::GnuVersym => { writer.write_null_gnu_versym(); for out_dynsym in &out_dynsyms { let symbol = self.dynamic_symbols.get(out_dynsym.id); let mut index = symbol.version.0 as u16; if symbol.version_hidden { index |= elf::VERSYM_HIDDEN; } writer.write_gnu_versym(index); } } SectionData::GnuVerdef => { writer.write_align_gnu_verdef(); if let Some(version_base) = &self.version_base { let verdef = write::elf::Verdef { version: elf::VER_DEF_CURRENT, flags: elf::VER_FLG_BASE, index: 1, aux_count: 1, name: writer.get_dynamic_string(version_base), }; if verdef_shared_base { writer.write_gnu_verdef_shared(&verdef); } else { writer.write_gnu_verdef(&verdef); } } for version in &self.versions { if let VersionData::Def(def) = &version.data { let mut names = def.names.iter(); let name = names.next().ok_or_else(|| { Error(format!("Missing SHT_GNU_VERDEF name {}", version.id.0)) })?; writer.write_gnu_verdef(&write::elf::Verdef { version: elf::VER_DEF_CURRENT, flags: def.flags, index: version.id.0 as u16, aux_count: def.names.len() as u16, name: writer.get_dynamic_string(name), }); for name in names { writer.write_gnu_verdaux(writer.get_dynamic_string(name)); } } } } SectionData::GnuVerneed => { writer.write_align_gnu_verneed(); for file in &self.version_files { let out_file = &out_version_files[file.id.0]; if out_file.versions.is_empty() { continue; } writer.write_gnu_verneed(&write::elf::Verneed { version: elf::VER_NEED_CURRENT, aux_count: out_file.versions.len() as u16, file: writer.get_dynamic_string(&file.name), }); for id in &out_file.versions { let version = self.versions.get(*id); // This will always match. if let VersionData::Need(need) = &version.data { debug_assert_eq!(*id, version.id); writer.write_gnu_vernaux(&write::elf::Vernaux { flags: need.flags, index: version.id.0 as u16, name: writer.get_dynamic_string(&need.name), }); } } } } _ => { return Err(Error(format!( "Unsupported alloc section type {:x}", section.sh_type ))); } } } } // Write non-alloc sections. for out_section in &mut out_sections { let section = self.sections.get(out_section.id); if !self.segments.is_empty() && section.is_alloc() { continue; } match §ion.data { SectionData::Data(data) => { writer.write_align(section.sh_addralign as usize); debug_assert_eq!(out_section.offset, writer.len()); writer.write(data); } SectionData::UninitializedData(_) => { // Nothing to do. } SectionData::Note(data) => { writer.write_align(section.sh_addralign as usize); debug_assert_eq!(out_section.offset, writer.len()); writer.write(data); } SectionData::Attributes(_) => { writer.write_align(section.sh_addralign as usize); debug_assert_eq!(out_section.offset, writer.len()); writer.write(&out_section.attributes); } // These are handled elsewhere. SectionData::Relocation(_) | SectionData::SectionString | SectionData::Symbol | SectionData::SymbolSectionIndex | SectionData::String => {} _ => { return Err(Error(format!( "Unsupported non-alloc section type {:x}", section.sh_type ))); } } } writer.write_null_symbol(); for out_sym in &out_syms { let symbol = self.symbols.get(out_sym.id); let section = symbol.section.map(|id| out_sections_index[id.0].unwrap()); writer.write_symbol(&write::elf::Sym { name: out_sym.name, section, st_info: symbol.st_info, st_other: symbol.st_other, st_shndx: symbol.st_shndx, st_value: symbol.st_value, st_size: symbol.st_size, }); } writer.write_symtab_shndx(); writer.write_strtab(); // Write non-alloc relocations. for section in &self.sections { if !self.segments.is_empty() && section.is_alloc() { continue; } let SectionData::Relocation(relocations) = §ion.data else { continue; }; writer.write_align_relocation(); for rel in relocations { let r_sym = if let Some(id) = rel.symbol { out_syms_index[id.0].unwrap().0 } else { 0 }; writer.write_relocation( section.sh_type == elf::SHT_RELA, &write::elf::Rel { r_offset: rel.r_offset, r_sym, r_type: rel.r_type, r_addend: rel.r_addend, }, ); } } writer.write_shstrtab(); writer.write_null_section_header(); for out_section in &out_sections { let section = self.sections.get(out_section.id); match §ion.data { SectionData::Data(_) | SectionData::UninitializedData(_) | SectionData::Relocation(_) | SectionData::DynamicRelocation(_) | SectionData::Note(_) | SectionData::Dynamic(_) | SectionData::Attributes(_) => { let sh_size = match §ion.data { SectionData::Data(data) => data.len() as u64, SectionData::UninitializedData(len) => *len, SectionData::Relocation(relocations) => { (relocations.len() * self.class().rel_size(section.sh_type == elf::SHT_RELA)) as u64 } SectionData::DynamicRelocation(relocations) => { (relocations.len() * self.class().rel_size(section.sh_type == elf::SHT_RELA)) as u64 } SectionData::Note(data) => data.len() as u64, SectionData::Dynamic(dynamics) => { ((1 + dynamics.len()) * self.class().dyn_size()) as u64 } SectionData::Attributes(_) => out_section.attributes.len() as u64, _ => { return Err(Error(format!( "Unimplemented size for section type {:x}", section.sh_type ))) } }; let sh_link = if let Some(id) = section.sh_link_section { if let Some(index) = out_sections_index[id.0] { index.0 } else { return Err(Error(format!( "Invalid sh_link from section '{}' to deleted section '{}'", section.name, self.sections.get(id).name, ))); } } else { 0 }; let sh_info = if let Some(id) = section.sh_info_section { if let Some(index) = out_sections_index[id.0] { index.0 } else { return Err(Error(format!( "Invalid sh_info link from section '{}' to deleted section '{}'", section.name, self.sections.get(id).name, ))); } } else { section.sh_info }; writer.write_section_header(&write::elf::SectionHeader { name: out_section.name, sh_type: section.sh_type, sh_flags: section.sh_flags, sh_addr: section.sh_addr, sh_offset: out_section.offset as u64, sh_size, sh_link, sh_info, sh_addralign: section.sh_addralign, sh_entsize: section.sh_entsize, }); } SectionData::SectionString => { writer.write_shstrtab_section_header(); } SectionData::Symbol => { writer.write_symtab_section_header(1 + num_local as u32); } SectionData::SymbolSectionIndex => { writer.write_symtab_shndx_section_header(); } SectionData::String => { writer.write_strtab_section_header(); } SectionData::DynamicString => { writer.write_dynstr_section_header(section.sh_addr); } SectionData::DynamicSymbol => { writer .write_dynsym_section_header(section.sh_addr, 1 + num_local_dynamic as u32); } SectionData::Hash => { writer.write_hash_section_header(section.sh_addr); } SectionData::GnuHash => { writer.write_gnu_hash_section_header(section.sh_addr); } SectionData::GnuVersym => { writer.write_gnu_versym_section_header(section.sh_addr); } SectionData::GnuVerdef => { writer.write_gnu_verdef_section_header(section.sh_addr); } SectionData::GnuVerneed => { writer.write_gnu_verneed_section_header(section.sh_addr); } } } debug_assert_eq!(writer.reserved_len(), writer.len()); Ok(()) } /// Delete segments, symbols, relocations, and dynamics that refer /// to deleted items. /// /// This calls `delete_orphan_segments`, `delete_orphan_symbols`, /// `delete_orphan_relocations`, and `delete_orphan_dynamics`. pub fn delete_orphans(&mut self) { self.delete_orphan_segments(); self.delete_orphan_symbols(); self.delete_orphan_relocations(); self.delete_orphan_dynamics(); } /// Set the delete flag for segments that only refer to deleted sections. pub fn delete_orphan_segments(&mut self) { let sections = &self.sections; for segment in &mut self.segments { // We only delete segments that have become empty due to section deletions. if segment.sections.is_empty() { continue; } segment.sections.retain(|id| !sections.get(*id).delete); segment.delete = segment.sections.is_empty(); } } /// Set the delete flag for symbols that refer to deleted sections. pub fn delete_orphan_symbols(&mut self) { for symbol in &mut self.symbols { if let Some(section) = symbol.section { if self.sections.get_mut(section).delete { symbol.delete = true; } } } for symbol in &mut self.dynamic_symbols { if let Some(section) = symbol.section { if self.sections.get_mut(section).delete { symbol.delete = true; } } } } /// Delete relocations that refer to deleted symbols. pub fn delete_orphan_relocations(&mut self) { let symbols = &self.symbols; let dynamic_symbols = &self.dynamic_symbols; for section in &mut self.sections { match &mut section.data { SectionData::Relocation(relocations) => { relocations.retain(|relocation| match relocation.symbol { None => true, Some(id) => !symbols.get(id).delete, }); } SectionData::DynamicRelocation(relocations) => { relocations.retain(|relocation| match relocation.symbol { None => true, Some(id) => !dynamic_symbols.get(id).delete, }); } _ => {} } } } /// Delete dynamic entries that refer to deleted sections. pub fn delete_orphan_dynamics(&mut self) { let mut have_dynsym = false; let mut have_dynstr = false; let mut have_hash = false; let mut have_gnu_hash = false; let mut have_versym = false; let mut have_verdef = false; let mut have_verneed = false; for section in &self.sections { match §ion.data { SectionData::DynamicSymbol => have_dynsym = true, SectionData::DynamicString => have_dynstr = true, SectionData::Hash => have_hash = true, SectionData::GnuHash => have_gnu_hash = true, SectionData::GnuVersym => have_versym = true, SectionData::GnuVerdef => have_verdef = true, SectionData::GnuVerneed => have_verneed = true, _ => {} } } for section in &mut self.sections { if let SectionData::Dynamic(dynamics) = &mut section.data { dynamics.retain(|dynamic| match dynamic { Dynamic::Auto { tag: elf::DT_SYMTAB, } => have_dynsym, Dynamic::Auto { tag: elf::DT_STRTAB, } | Dynamic::Auto { tag: elf::DT_STRSZ } => have_dynstr, Dynamic::Auto { tag: elf::DT_HASH } => have_hash, Dynamic::Auto { tag: elf::DT_GNU_HASH, } => have_gnu_hash, Dynamic::Auto { tag: elf::DT_VERSYM, } => have_versym, Dynamic::Auto { tag: elf::DT_VERNEED, } | Dynamic::Auto { tag: elf::DT_VERNEEDNUM, } => have_verneed, Dynamic::Auto { tag: elf::DT_VERDEF, } | Dynamic::Auto { tag: elf::DT_VERDEFNUM, } => have_verdef, _ => true, }); } } } /// Delete unused GNU version entries. pub fn delete_unused_versions(&mut self) { let mut version_used = vec![false; self.versions.len() + VERSION_ID_BASE]; for symbol in &self.dynamic_symbols { version_used[symbol.version.0] = true; } let mut version_file_used = vec![false; self.version_files.len()]; for version in &mut self.versions { if !version_used[version.id.0] { version.delete = true; continue; } if let VersionData::Need(need) = &version.data { version_file_used[need.file.0] = true; } } for file in &mut self.version_files { if !version_file_used[file.id.0] { file.delete = true; } } } /// Return the ELF file class that will be written. /// /// This can be useful for calculating sizes. pub fn class(&self) -> write::elf::Class { write::elf::Class { is_64: self.is_64 } } /// Calculate the size of the file header. pub fn file_header_size(&self) -> usize { self.class().file_header_size() } /// Calculate the size of the program headers. pub fn program_headers_size(&self) -> usize { self.segments.count() * self.class().program_header_size() } /// Calculate the size of the dynamic symbol table. /// /// To get an accurate result, you may need to first call /// [`Self::delete_orphan_symbols`]. pub fn dynamic_symbol_size(&self) -> usize { (1 + self.dynamic_symbols.count()) * self.class().sym_size() } /// Calculate the size of the dynamic string table. /// /// This adds all of the currently used dynamic strings to a string table, /// calculates the size of the string table, and discards the string table. /// /// To get an accurate result, you may need to first call /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. pub fn dynamic_string_size(&self) -> usize { let mut dynstr = write::string::StringTable::default(); for section in &self.sections { if let SectionData::Dynamic(dynamics) = §ion.data { for dynamic in dynamics { if let Dynamic::String { val, .. } = dynamic { dynstr.add(val); } } } } for symbol in &self.dynamic_symbols { dynstr.add(&symbol.name); } if let Some(version_base) = &self.version_base { dynstr.add(version_base); } for version in &self.versions { match &version.data { VersionData::Def(def) => { for name in &def.names { dynstr.add(name); } } VersionData::Need(need) => { dynstr.add(&need.name); } } } for file in &self.version_files { dynstr.add(&file.name); } dynstr.size(1) } /// Calculate the size of the hash table. /// /// To get an accurate result, you may need to first call /// [`Self::delete_orphan_symbols`]. pub fn hash_size(&self) -> usize { let chain_count = 1 + self.dynamic_symbols.count(); self.class() .hash_size(self.hash_bucket_count, chain_count as u32) } /// Calculate the size of the GNU hash table. /// /// To get an accurate result, you may need to first call /// [`Self::delete_orphan_symbols`]. pub fn gnu_hash_size(&self) -> usize { let symbol_count = self.dynamic_symbols.count_defined(); self.class().gnu_hash_size( self.gnu_hash_bloom_count, self.gnu_hash_bucket_count, symbol_count as u32, ) } /// Calculate the size of the GNU symbol version section. /// /// To get an accurate result, you may need to first call /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. pub fn gnu_versym_size(&self) -> usize { let symbol_count = 1 + self.dynamic_symbols.count(); self.class().gnu_versym_size(symbol_count) } /// Calculate the size of the GNU version definition section. /// /// To get an accurate result, you may need to first call /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. pub fn gnu_verdef_size(&self) -> usize { let mut verdef_count = 0; let mut verdaux_count = 0; if self.version_base.is_some() { verdef_count += 1; verdaux_count += 1; } for version in &self.versions { if let VersionData::Def(def) = &version.data { if !def.is_shared(verdef_count, self.version_base.as_ref()) { verdaux_count += def.names.len(); } verdef_count += 1; } } self.class().gnu_verdef_size(verdef_count, verdaux_count) } /// Calculate the size of the GNU version dependency section. /// /// To get an accurate result, you may need to first call /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. pub fn gnu_verneed_size(&self) -> usize { let verneed_count = self.version_files.count(); let mut vernaux_count = 0; for version in &self.versions { if let VersionData::Need(_) = &version.data { vernaux_count += 1; } } self.class().gnu_verneed_size(verneed_count, vernaux_count) } /// Calculate the memory size of a section. /// /// Returns 0 for sections that are deleted or aren't allocated. /// /// To get an accurate result, you may need to first call /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. pub fn section_size(&self, section: &Section<'_>) -> usize { if section.delete || !section.is_alloc() { return 0; } match §ion.data { SectionData::Data(data) => data.len(), SectionData::UninitializedData(len) => *len as usize, SectionData::Relocation(relocations) => { relocations.len() * self.class().rel_size(section.sh_type == elf::SHT_RELA) } SectionData::DynamicRelocation(relocations) => { relocations.len() * self.class().rel_size(section.sh_type == elf::SHT_RELA) } SectionData::Note(data) => data.len(), SectionData::Dynamic(dynamics) => (1 + dynamics.len()) * self.class().dyn_size(), SectionData::DynamicString => self.dynamic_string_size(), SectionData::DynamicSymbol => self.dynamic_symbol_size(), SectionData::Hash => self.hash_size(), SectionData::GnuHash => self.gnu_hash_size(), SectionData::GnuVersym => self.gnu_versym_size(), SectionData::GnuVerdef => self.gnu_verdef_size(), SectionData::GnuVerneed => self.gnu_verneed_size(), // None of these should be allocated. SectionData::SectionString | SectionData::Symbol | SectionData::SymbolSectionIndex | SectionData::String | SectionData::Attributes(_) => 0, } } /// Set the `sh_size` field for every allocated section. /// /// This is useful to call prior to doing memory layout. /// /// To get an accurate result, you may need to first call /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. pub fn set_section_sizes(&mut self) { for id in (0..self.sections.len()).map(SectionId) { let section = self.sections.get(id); if section.delete || !section.is_alloc() { continue; } self.sections.get_mut(id).sh_size = self.section_size(section) as u64; } } /// Find the section containing the dynamic table. /// /// This uses the `PT_DYNAMIC` program header to find the dynamic section. pub fn dynamic_section(&self) -> Option { let segment = self .segments .iter() .find(|segment| segment.p_type == elf::PT_DYNAMIC)?; // TODO: handle multiple sections in the segment? segment.sections.iter().copied().next() } /// Find the dynamic table entries. /// /// This uses the `PT_DYNAMIC` program header to find the dynamic section, pub fn dynamic_data(&self) -> Option<&[Dynamic<'data>]> { let section = self.dynamic_section()?; match &self.sections.get(section).data { SectionData::Dynamic(dynamics) => Some(dynamics), _ => None, } } /// Find the dynamic table entries. /// /// This uses the `PT_DYNAMIC` program header to find the dynamic section, pub fn dynamic_data_mut(&mut self) -> Option<&mut Vec>> { let section = self.dynamic_section()?; match &mut self.sections.get_mut(section).data { SectionData::Dynamic(dynamics) => Some(dynamics), _ => None, } } /// Find the section containing the interpreter path. /// /// This uses the `PT_INTERP` program header to find the interp section. pub fn interp_section(&self) -> Option { let segment = self .segments .iter() .find(|segment| segment.p_type == elf::PT_INTERP)?; // TODO: handle multiple sections in the segment? segment.sections.iter().copied().next() } /// Find the interpreter path. /// /// This uses the `PT_INTERP` program header to find the interp section. pub fn interp_data(&self) -> Option<&[u8]> { let section = self.interp_section()?; match &self.sections.get(section).data { SectionData::Data(data) => Some(data), _ => None, } } /// Find the interpreter path. /// /// This uses the `PT_INTERP` program header to find the interp section. pub fn interp_data_mut(&mut self) -> Option<&mut Bytes<'data>> { let section = self.interp_section()?; match &mut self.sections.get_mut(section).data { SectionData::Data(data) => Some(data), _ => None, } } } /// ELF file header. /// /// This corresponds to fields in [`elf::FileHeader32`] or [`elf::FileHeader64`]. /// This only contains the ELF file header fields that can be modified. /// The other fields are automatically calculated. #[derive(Debug, Default)] pub struct Header { /// The OS ABI field in the file header. /// /// One of the `ELFOSABI*` constants. pub os_abi: u8, /// The ABI version field in the file header. /// /// The meaning of this field depends on the `os_abi` value. pub abi_version: u8, /// The object file type in the file header. /// /// One of the `ET_*` constants. pub e_type: u16, /// The architecture in the file header. /// /// One of the `EM_*` constants. pub e_machine: u16, /// Entry point virtual address in the file header. pub e_entry: u64, /// The processor-specific flags in the file header. /// /// A combination of the `EF_*` constants. pub e_flags: u32, /// The file offset of the program header table. /// /// Writing will fail if the program header table cannot be placed at this offset. pub e_phoff: u64, } /// An ID for referring to a segment in [`Segments`]. #[derive(Clone, Copy, PartialEq, Eq)] pub struct SegmentId(usize); impl fmt::Debug for SegmentId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "SegmentId({})", self.0) } } impl Id for SegmentId { fn index(&self) -> usize { self.0 } } impl IdPrivate for SegmentId { fn new(id: usize) -> Self { SegmentId(id) } } /// A segment in [`Segments`]. /// /// This corresponds to [`elf::ProgramHeader32`] or [`elf::ProgramHeader64`]. #[derive(Debug)] pub struct Segment<'data> { id: SegmentId, /// Ignore this segment when writing the ELF file. pub delete: bool, /// The `p_type` field in the ELF program header. /// /// One of the `PT_*` constants. pub p_type: u32, /// The `p_flags` field in the ELF program header. /// /// A combination of the `PF_*` constants. pub p_flags: u32, /// The `p_offset` field in the ELF program header. /// /// This is the file offset of the data in the segment. This should /// correspond to the file offset of the sections that are placed in /// this segment. Currently there is no support for section data /// that is not contained in sections. pub p_offset: u64, /// The `p_vaddr` field in the ELF program header. pub p_vaddr: u64, /// The `p_paddr` field in the ELF program header. pub p_paddr: u64, /// The `p_filesz` field in the ELF program header. pub p_filesz: u64, /// The `p_memsz` field in the ELF program header. pub p_memsz: u64, /// The `p_align` field in the ELF program header. pub p_align: u64, /// The sections contained in this segment. pub sections: Vec, // Might need to add reference to data if no sections. marker: PhantomData<&'data ()>, } impl<'data> Item for Segment<'data> { type Id = SegmentId; fn is_deleted(&self) -> bool { self.delete } } impl<'data> Segment<'data> { /// The ID used for referring to this segment. pub fn id(&self) -> SegmentId { self.id } /// Returns true if the segment type is `PT_LOAD`. pub fn is_load(&self) -> bool { self.p_type == elf::PT_LOAD } /// Returns true if the segment contains the given file offset. pub fn contains_offset(&self, offset: u64) -> bool { offset >= self.p_offset && offset - self.p_offset < self.p_filesz } /// Return the address corresponding to the given file offset. /// /// This will return a meaningless value if `contains_offset` is false. pub fn address_from_offset(&self, offset: u64) -> u64 { self.p_vaddr .wrapping_add(offset.wrapping_sub(self.p_offset)) } /// Returns true if the segment contains the given address. pub fn contains_address(&self, address: u64) -> bool { address >= self.p_vaddr && address - self.p_vaddr < self.p_memsz } /// Remove all sections from the segment, and set its size to zero. pub fn remove_sections(&mut self) { self.p_filesz = 0; self.p_memsz = 0; self.sections.clear(); } /// Add a section to the segment. /// /// If this is a [`elf::PT_LOAD`] segment, then the file offset and address of the /// section is changed to be at the end of the segment. /// /// The segment's file and address ranges are extended to include the section. /// This uses the `sh_size` field of the section, not the size of the section data. /// /// The section's id is added to the segment's list of sections. pub fn append_section(&mut self, section: &mut Section<'_>) { debug_assert_eq!(self.p_filesz, self.p_memsz); if self.p_type == elf::PT_LOAD { let align = section.sh_addralign; let offset = (self.p_offset + self.p_filesz + (align - 1)) & !(align - 1); let addr = (self.p_paddr + self.p_memsz + (align - 1)) & !(align - 1); section.sh_offset = offset; section.sh_addr = addr; } self.append_section_range(section); self.sections.push(section.id); } /// Extend this segment's file and address ranges to include the given section. /// /// If the segment's `p_memsz` is zero, then this signifies that the segment /// has no file or address range yet. In this case, the segment's file and address /// ranges are set equal to the section. Otherwise, the segment's file and address /// ranges are extended to include the section. /// /// This uses the `sh_size` field of the section, not the size of the section data. pub fn append_section_range(&mut self, section: &Section<'_>) { let section_filesize = if section.sh_type == elf::SHT_NOBITS { 0 } else { section.sh_size }; if self.p_memsz == 0 { self.p_offset = section.sh_offset; self.p_filesz = section_filesize; self.p_vaddr = section.sh_addr; self.p_paddr = section.sh_addr; self.p_memsz = section.sh_size; } else { if self.p_offset > section.sh_offset { self.p_offset = section.sh_offset; } let filesz = section.sh_offset + section_filesize - self.p_offset; if self.p_filesz < filesz { self.p_filesz = filesz; } if self.p_vaddr > section.sh_addr { self.p_vaddr = section.sh_addr; self.p_paddr = section.sh_addr; } let memsz = section.sh_addr + section.sh_size - self.p_vaddr; if self.p_memsz < memsz { self.p_memsz = memsz; } } } /// Recalculate the file and address ranges of the segment. /// /// Resets the segment's file and address ranges to zero, and then /// calls `append_section_range` for each section in the segment. pub fn recalculate_ranges(&mut self, sections: &Sections<'data>) { self.p_offset = 0; self.p_filesz = 0; self.p_vaddr = 0; self.p_paddr = 0; self.p_memsz = 0; let ids = core::mem::take(&mut self.sections); for id in &ids { let section = sections.get(*id); self.append_section_range(section); } self.sections = ids; } } /// A segment table. pub type Segments<'data> = Table>; impl<'data> Segments<'data> { /// Add a new segment to the table. pub fn add(&mut self) -> &mut Segment<'data> { let id = self.next_id(); self.push(Segment { id, delete: false, p_type: 0, p_flags: 0, p_offset: 0, p_vaddr: 0, p_paddr: 0, p_filesz: 0, p_memsz: 0, p_align: 0, sections: Vec::new(), marker: PhantomData, }); self.get_mut(id) } /// Find a `PT_LOAD` segment containing the given offset. pub fn find_load_segment_from_offset(&self, offset: u64) -> Option<&Segment<'data>> { self.iter() .find(|segment| segment.is_load() && segment.contains_offset(offset)) } /// Add a new `PT_LOAD` segment to the table. /// /// The file offset and address will be derived from the current maximum for any segment. /// The address will be chosen so that `p_paddr % align == p_offset % align`. /// You may wish to use [`Builder::load_align`] for the alignment. pub fn add_load_segment(&mut self, flags: u32, align: u64) -> &mut Segment<'data> { let mut max_offset = 0; let mut max_addr = 0; for segment in &*self { let offset = segment.p_offset + segment.p_filesz; if max_offset < offset { max_offset = offset; } let addr = segment.p_vaddr + segment.p_memsz; if max_addr < addr { max_addr = addr; } } // No alignment is required for the segment file offset because sections // will add their alignment to the file offset when they are added. let offset = max_offset; // The address must be chosen so that addr % align == offset % align. let addr = ((max_addr + (align - 1)) & !(align - 1)) + (offset & (align - 1)); let segment = self.add(); segment.p_type = elf::PT_LOAD; segment.p_flags = flags; segment.p_offset = offset; segment.p_vaddr = addr; segment.p_paddr = addr; segment.p_align = align; segment } /// Add a copy of a segment to the table. /// /// This will copy the segment type, flags and alignment. /// /// Additionally, if the segment type is `PT_LOAD`, then the file offset and address /// will be set as in `add_load_segment`. pub fn copy(&mut self, id: SegmentId) -> &mut Segment<'data> { let segment = self.get(id); let p_type = segment.p_type; let p_flags = segment.p_flags; let p_align = segment.p_align; if p_type == elf::PT_LOAD { self.add_load_segment(p_flags, p_align) } else { let segment = self.add(); segment.p_type = p_type; segment.p_flags = p_flags; segment.p_align = p_align; segment } } } /// An ID for referring to a section in [`Sections`]. #[derive(Clone, Copy, PartialEq, Eq)] pub struct SectionId(usize); impl fmt::Debug for SectionId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "SectionId({})", self.0) } } impl Id for SectionId { fn index(&self) -> usize { self.0 } } impl IdPrivate for SectionId { fn new(id: usize) -> Self { SectionId(id) } } /// A section in [`Sections`]. /// /// This corresponds to [`elf::SectionHeader32`] or [`elf::SectionHeader64`]. #[derive(Debug)] pub struct Section<'data> { id: SectionId, /// Ignore this section when writing the ELF file. pub delete: bool, /// The name of the section. /// /// This is automatically added to the section header string table, /// and the resulting string table offset is used to set the `sh_name` /// field in the ELF section header. pub name: ByteString<'data>, /// The `sh_type` field in the ELF section header. /// /// One of the `SHT_*` constants. pub sh_type: u32, /// The `sh_flags` field in the ELF section header. /// /// A combination of the `SHF_*` constants. pub sh_flags: u64, /// The `sh_addr` field in the ELF section header. pub sh_addr: u64, /// The `sh_offset` field in the ELF section header. /// /// This is the file offset of the data in the section. /// Writing will fail if the data cannot be placed at this offset. /// /// This is only used for sections that have `SHF_ALLOC` set. /// For other sections, the section data is written at the next available /// offset. pub sh_offset: u64, /// The `sh_size` field in the ELF section header. /// /// This size is not used when writing. The size of the `data` field is /// used instead. pub sh_size: u64, /// The ID of the section linked to by the `sh_link` field in the ELF section header. pub sh_link_section: Option, /// The `sh_info` field in the ELF section header. /// /// Only used if `sh_info_section` is `None`. pub sh_info: u32, /// The ID of the section linked to by the `sh_info` field in the ELF section header. pub sh_info_section: Option, /// The `sh_addralign` field in the ELF section header. pub sh_addralign: u64, /// The `sh_entsize` field in the ELF section header. pub sh_entsize: u64, /// The section data. pub data: SectionData<'data>, } impl<'data> Item for Section<'data> { type Id = SectionId; fn is_deleted(&self) -> bool { self.delete } } impl<'data> Section<'data> { /// The ID used for referring to this section. pub fn id(&self) -> SectionId { self.id } /// Returns true if the section flags include `SHF_ALLOC`. pub fn is_alloc(&self) -> bool { self.sh_flags & u64::from(elf::SHF_ALLOC) != 0 } /// Return the segment permission flags that are equivalent to the section flags. pub fn p_flags(&self) -> u32 { let mut p_flags = elf::PF_R; if self.sh_flags & u64::from(elf::SHF_WRITE) != 0 { p_flags |= elf::PF_W; } if self.sh_flags & u64::from(elf::SHF_EXECINSTR) != 0 { p_flags |= elf::PF_X; } p_flags } } /// The data for a [`Section`]. #[derive(Debug, Clone)] pub enum SectionData<'data> { /// The section contains the given raw data bytes. Data(Bytes<'data>), /// The section contains uninitialised data bytes of the given length. UninitializedData(u64), /// The section contains relocations. Relocation(Vec), /// The section contains dynamic relocations. DynamicRelocation(Vec), /// The section contains notes. // TODO: parse notes Note(Bytes<'data>), /// The section contains dynamic entries. Dynamic(Vec>), /// The section contains attributes. /// /// This may be GNU attributes or other vendor-specific attributes. Attributes(AttributesSection<'data>), /// The section contains the strings for the section headers. SectionString, /// The section contains the symbol table. Symbol, /// The section contains the extended section index for the symbol table. SymbolSectionIndex, /// The section contains the strings for symbol table. String, /// The section contains the dynamic symbol table. DynamicSymbol, /// The section contains the dynamic string table. DynamicString, /// The section contains the hash table. Hash, /// The section contains the GNU hash table. GnuHash, /// The section contains the GNU symbol versions. GnuVersym, /// The section contains the GNU version definitions. GnuVerdef, /// The section contains the GNU version dependencies. GnuVerneed, } /// A section table. pub type Sections<'data> = Table>; impl<'data> Sections<'data> { /// Add a new section to the table. pub fn add(&mut self) -> &mut Section<'data> { let id = self.next_id(); self.push(Section { id, delete: false, name: ByteString::default(), sh_type: 0, sh_flags: 0, sh_addr: 0, sh_offset: 0, sh_size: 0, sh_link_section: None, sh_info: 0, sh_info_section: None, sh_addralign: 0, sh_entsize: 0, data: SectionData::Data(Bytes::default()), }) } /// Add a copy of a section to the table. /// /// This will set the file offset of the copy to zero. /// [`Segment::append_section`] can be used to assign a valid file offset and a new address. pub fn copy(&mut self, id: SectionId) -> &mut Section<'data> { let section = self.get(id); let id = self.next_id(); let name = section.name.clone(); let sh_type = section.sh_type; let sh_flags = section.sh_flags; let sh_addr = section.sh_addr; let sh_size = section.sh_size; let sh_link_section = section.sh_link_section; let sh_info = section.sh_info; let sh_info_section = section.sh_info_section; let sh_addralign = section.sh_addralign; let sh_entsize = section.sh_entsize; let data = section.data.clone(); self.push(Section { id, delete: false, name, sh_type, sh_flags, sh_addr, sh_offset: 0, sh_size, sh_link_section, sh_info, sh_info_section, sh_addralign, sh_entsize, data, }) } } /// An ID for referring to a symbol in [`Symbols`]. #[derive(Clone, Copy, PartialEq, Eq)] pub struct SymbolId(usize); impl fmt::Debug for SymbolId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } impl Id for SymbolId { fn index(&self) -> usize { self.0 } } impl IdPrivate for SymbolId { fn new(id: usize) -> Self { SymbolId(id) } } /// A symbol in [`Symbols`]. /// /// This corresponds to [`elf::Sym32`] or [`elf::Sym64`]. #[derive(Debug)] pub struct Symbol<'data, const DYNAMIC: bool = false> { id: SymbolId, /// Ignore this symbol when writing the ELF file. pub delete: bool, /// The name of the symbol. pub name: ByteString<'data>, /// The section referenced by the symbol. /// /// Used to set the `st_shndx` field in the ELF symbol. pub section: Option, /// The `st_info` field in the ELF symbol. pub st_info: u8, /// The `st_other` field in the ELF symbol. pub st_other: u8, /// The `st_shndx` field in the ELF symbol. /// /// Only used if `Self::section` is `None`. pub st_shndx: u16, /// The `st_value` field in the ELF symbol. pub st_value: u64, /// The `st_size` field in the ELF symbol. pub st_size: u64, /// GNU version for dynamic symbols. pub version: VersionId, /// Set the [`elf::VERSYM_HIDDEN`] flag for this symbol. pub version_hidden: bool, } impl<'data, const DYNAMIC: bool> Item for Symbol<'data, DYNAMIC> { type Id = SymbolId; fn is_deleted(&self) -> bool { self.delete } } impl<'data, const DYNAMIC: bool> Symbol<'data, DYNAMIC> { /// The ID used for referring to this symbol. pub fn id(&self) -> SymbolId { self.id } /// Get the `st_bind` component of the `st_info` field. #[inline] pub fn st_bind(&self) -> u8 { self.st_info >> 4 } /// Get the `st_type` component of the `st_info` field. #[inline] pub fn st_type(&self) -> u8 { self.st_info & 0xf } /// Set the `st_info` field given the `st_bind` and `st_type` components. #[inline] pub fn set_st_info(&mut self, st_bind: u8, st_type: u8) { self.st_info = (st_bind << 4) + (st_type & 0xf); } } /// A symbol table. pub type Symbols<'data, const DYNAMIC: bool = false> = Table>; impl<'data, const DYNAMIC: bool> Symbols<'data, DYNAMIC> { /// Number of defined symbols. pub fn count_defined(&self) -> usize { self.into_iter() .filter(|symbol| symbol.st_shndx != elf::SHN_UNDEF) .count() } /// Add a new symbol to the table. pub fn add(&mut self) -> &mut Symbol<'data, DYNAMIC> { let id = self.next_id(); self.push(Symbol { id, delete: false, name: ByteString::default(), section: None, st_info: 0, st_other: 0, st_shndx: 0, st_value: 0, st_size: 0, version: VersionId::local(), version_hidden: false, }) } } /// A relocation stored in a [`Section`]. /// /// This corresponds to [`elf::Rel32`], [`elf::Rela32`], [`elf::Rel64`] or [`elf::Rela64`]. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Relocation { /// The `r_offset` field in the ELF relocation. pub r_offset: u64, /// The symbol referenced by the ELF relocation. pub symbol: Option>, /// The `r_type` field in the ELF relocation. pub r_type: u32, /// The `r_addend` field in the ELF relocation. /// /// Only used if the section type is `SHT_RELA`. pub r_addend: i64, } /// A dynamic symbol ID. pub type DynamicSymbolId = SymbolId; /// A dynamic symbol. pub type DynamicSymbol<'data> = Symbol<'data, true>; /// A dynamic symbol table. pub type DynamicSymbols<'data> = Symbols<'data, true>; /// A dynamic relocation. pub type DynamicRelocation = Relocation; /// An entry in the dynamic section. /// /// This corresponds to [`elf::Dyn32`] or [`elf::Dyn64`]. #[derive(Debug, Clone, PartialEq, Eq)] pub enum Dynamic<'data> { /// The value is an automatically generated integer. /// /// Writing will fail if the value cannot be automatically generated. Auto { /// The `d_tag` field in the dynamic entry. /// /// One of the `DT_*` values. tag: u32, }, /// The value is an integer. Integer { /// The `d_tag` field in the dynamic entry. /// /// One of the `DT_*` values. tag: u32, /// The `d_val` field in the dynamic entry. val: u64, }, /// The value is a string. String { /// The `d_tag` field in the dynamic entry. /// /// One of the `DT_*` values. tag: u32, /// The string value. /// /// This will be stored in the dynamic string section. val: ByteString<'data>, }, } impl<'data> Dynamic<'data> { /// The `d_tag` field in the dynamic entry. /// /// One of the `DT_*` values. pub fn tag(&self) -> u32 { match self { Dynamic::Auto { tag } => *tag, Dynamic::Integer { tag, .. } => *tag, Dynamic::String { tag, .. } => *tag, } } } /// An ID for referring to a filename in [`VersionFiles`]. #[derive(Clone, Copy, PartialEq, Eq)] pub struct VersionFileId(usize); impl fmt::Debug for VersionFileId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "VersionFileId({})", self.0) } } impl Id for VersionFileId { fn index(&self) -> usize { self.0 } } impl IdPrivate for VersionFileId { fn new(id: usize) -> Self { VersionFileId(id) } } /// A filename used for GNU versioning. /// /// Stored in [`VersionFiles`]. #[derive(Debug)] pub struct VersionFile<'data> { id: VersionFileId, /// Ignore this file when writing the ELF file. pub delete: bool, /// The filename. pub name: ByteString<'data>, } impl<'data> Item for VersionFile<'data> { type Id = VersionFileId; fn is_deleted(&self) -> bool { self.delete } } impl<'data> VersionFile<'data> { /// The ID used for referring to this filename. pub fn id(&self) -> VersionFileId { self.id } } /// A table of filenames used for GNU versioning. pub type VersionFiles<'data> = Table>; impl<'data> VersionFiles<'data> { /// Add a new filename to the table. pub fn add(&mut self, name: ByteString<'data>) -> VersionFileId { let id = self.next_id(); self.push(VersionFile { id, name, delete: false, }); id } } const VERSION_ID_BASE: usize = 2; /// An ID for referring to a version in [`Versions`]. #[derive(Clone, Copy, PartialEq, Eq)] pub struct VersionId(usize); impl fmt::Debug for VersionId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "VersionId({})", self.0) } } impl Id for VersionId { fn index(&self) -> usize { self.0 - VERSION_ID_BASE } } impl IdPrivate for VersionId { fn new(id: usize) -> Self { VersionId(VERSION_ID_BASE + id) } } impl VersionId { /// Return `True` if this is a special version that does not exist in the version table. pub fn is_special(&self) -> bool { self.0 < VERSION_ID_BASE } /// Return the ID for a version index of [`elf::VER_NDX_LOCAL`]. pub fn local() -> Self { VersionId(elf::VER_NDX_LOCAL as usize) } /// Return the ID for a version index of [`elf::VER_NDX_GLOBAL`]. pub fn global() -> Self { VersionId(elf::VER_NDX_GLOBAL as usize) } } /// A version for a symbol. #[derive(Debug)] pub struct Version<'data> { id: VersionId, /// The data for this version. pub data: VersionData<'data>, /// Ignore this version when writing the ELF file. pub delete: bool, } impl<'data> Item for Version<'data> { type Id = VersionId; fn is_deleted(&self) -> bool { self.delete } } impl<'data> Version<'data> { /// The ID used for referring to this version. pub fn id(&self) -> VersionId { self.id } } /// The data for a version for a symbol. #[derive(Debug)] pub enum VersionData<'data> { /// The version for a defined symbol. Def(VersionDef<'data>), /// The version for an undefined symbol. Need(VersionNeed<'data>), } /// A GNU version definition. #[derive(Debug)] pub struct VersionDef<'data> { /// The names for the version. /// /// This usually has two elements. The first element is the name of this /// version, and the second element is the name of the previous version /// in the tree of versions. pub names: Vec>, /// The version flags. /// /// A combination of the `VER_FLG_*` constants. pub flags: u16, } impl<'data> VersionDef<'data> { /// Optimise for the common case where the first version is the same as the base version. fn is_shared(&self, index: usize, base: Option<&ByteString<'_>>) -> bool { index == 1 && self.names.len() == 1 && self.names.first() == base } } /// A GNU version dependency. #[derive(Debug)] pub struct VersionNeed<'data> { /// The filename of the library providing this version. pub file: VersionFileId, /// The name of the version. pub name: ByteString<'data>, /// The version flags. /// /// A combination of the `VER_FLG_*` constants. pub flags: u16, } /// A table of versions that are referenced by symbols. pub type Versions<'data> = Table>; impl<'data> Versions<'data> { /// Add a version. pub fn add(&mut self, data: VersionData<'data>) -> VersionId { let id = self.next_id(); self.push(Version { id, data, delete: false, }); id } } /// The contents of an attributes section. #[derive(Debug, Default, Clone)] pub struct AttributesSection<'data> { /// The subsections. pub subsections: Vec>, } impl<'data> AttributesSection<'data> { /// Create a new attributes section. pub fn new() -> Self { Self::default() } } /// A subsection of an attributes section. #[derive(Debug, Clone)] pub struct AttributesSubsection<'data> { /// The vendor namespace for these attributes. pub vendor: ByteString<'data>, /// The sub-subsections. pub subsubsections: Vec>, } impl<'data> AttributesSubsection<'data> { /// Create a new subsection. pub fn new(vendor: ByteString<'data>) -> Self { AttributesSubsection { vendor, subsubsections: Vec::new(), } } } /// A sub-subsection in an attributes section. #[derive(Debug, Clone)] pub struct AttributesSubsubsection<'data> { /// The sub-subsection tag. pub tag: AttributeTag, /// The data containing the attributes. pub data: Bytes<'data>, } /// The tag for a sub-subsection in an attributes section. #[derive(Debug, Clone, PartialEq, Eq)] pub enum AttributeTag { /// The attributes apply to the whole file. /// /// Correspeonds to [`elf::Tag_File`]. File, /// The attributes apply to the given sections. /// /// Correspeonds to [`elf::Tag_Section`]. Section(Vec), /// The attributes apply to the given symbols. /// /// Correspeonds to [`elf::Tag_Symbol`]. Symbol(Vec), } impl AttributeTag { /// Return the corresponding `elf::Tag_*` value for this tag. pub fn tag(&self) -> u8 { match self { AttributeTag::File => elf::Tag_File, AttributeTag::Section(_) => elf::Tag_Section, AttributeTag::Symbol(_) => elf::Tag_Symbol, } } } object-0.36.5/src/build/error.rs000064400000000000000000000015731046102023000145600ustar 00000000000000use alloc::string::String; use core::{fmt, result}; #[cfg(feature = "std")] use std::error; use crate::{read, write}; /// The error type used within the build module. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Error(pub(super) String); impl Error { pub(super) fn new(message: impl Into) -> Self { Error(message.into()) } } impl fmt::Display for Error { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&self.0) } } #[cfg(feature = "std")] impl error::Error for Error {} impl From for Error { fn from(error: read::Error) -> Error { Error(format!("{}", error)) } } impl From for Error { fn from(error: write::Error) -> Error { Error(error.0) } } /// The result type used within the build module. pub type Result = result::Result; object-0.36.5/src/build/mod.rs000064400000000000000000000005731046102023000142050ustar 00000000000000//! Interface for building object files. //! //! This module provides common types and traits used in the builders. //! //! The submodules define the builders for each file format. mod error; pub use error::{Error, Result}; mod bytes; pub use bytes::{ByteString, Bytes}; mod table; use table::IdPrivate; pub use table::{Id, Item, Table}; #[cfg(feature = "elf")] pub mod elf; object-0.36.5/src/build/table.rs000064400000000000000000000057541046102023000145230ustar 00000000000000use alloc::vec::Vec; /// An item in a [`Table`]. pub trait Item { /// The type of identifier for the item. type Id: Id; /// Return `True` if the item is deleted. fn is_deleted(&self) -> bool; } /// An identifier for referring to an item in a [`Table`]. pub trait Id: IdPrivate { /// Return the index of the item in the table. fn index(&self) -> usize; } mod id_private { pub trait IdPrivate { fn new(id: usize) -> Self; } } pub(super) use id_private::IdPrivate; /// A table of items. /// /// Each item has a unique identifier. /// Items can be deleted without changing the identifiers of other items. #[derive(Debug)] pub struct Table(Vec); impl Table { pub(super) fn new() -> Self { Table(Vec::new()) } } impl Table { pub(super) fn next_id(&self) -> T::Id { T::Id::new(self.0.len()) } pub(super) fn push(&mut self, item: T) -> &mut T { self.0.push(item); self.0.last_mut().unwrap() } /// Number of items, including deleted items. pub(super) fn len(&self) -> usize { self.0.len() } /// Return `True` if there are no non-deleted items. pub fn is_empty(&self) -> bool { self.into_iter().next().is_none() } /// Number of non-deleted items. pub fn count(&self) -> usize { self.into_iter().count() } /// Return a reference to an item. pub fn get(&self, id: T::Id) -> &T { self.0.get(id.index()).unwrap() } /// Return a mutable reference to a segment. pub fn get_mut(&mut self, id: T::Id) -> &mut T { self.0.get_mut(id.index()).unwrap() } /// Return an iterator for the segments. pub fn iter(&self) -> TableIter<'_, T> { self.into_iter() } /// Return a mutable iterator for the segments. pub fn iter_mut(&mut self) -> TableIterMut<'_, T> { self.into_iter() } } impl<'a, T: Item> IntoIterator for &'a Table { type Item = &'a T; type IntoIter = TableIter<'a, T>; fn into_iter(self) -> TableIter<'a, T> { TableIter { iter: self.0.iter(), } } } impl<'a, T: Item> IntoIterator for &'a mut Table { type Item = &'a mut T; type IntoIter = TableIterMut<'a, T>; fn into_iter(self) -> TableIterMut<'a, T> { TableIterMut { iter: self.0.iter_mut(), } } } /// An iterator for non-deleted items in a [`Table`]. #[derive(Debug)] pub struct TableIter<'a, T> { iter: core::slice::Iter<'a, T>, } impl<'a, T: Item> Iterator for TableIter<'a, T> { type Item = &'a T; fn next(&mut self) -> Option<&'a T> { self.iter.find(|item| !item.is_deleted()) } } /// An iterator for non-deleted items in a [`Table`]. #[derive(Debug)] pub struct TableIterMut<'a, T> { iter: core::slice::IterMut<'a, T>, } impl<'a, T: Item> Iterator for TableIterMut<'a, T> { type Item = &'a mut T; fn next(&mut self) -> Option<&'a mut T> { self.iter.find(|item| !item.is_deleted()) } } object-0.36.5/src/common.rs000064400000000000000000000422121046102023000136130ustar 00000000000000/// A CPU architecture. #[allow(missing_docs)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum Architecture { Unknown, Aarch64, #[allow(non_camel_case_types)] Aarch64_Ilp32, Arm, Avr, Bpf, Csky, E2K32, E2K64, I386, X86_64, #[allow(non_camel_case_types)] X86_64_X32, Hexagon, LoongArch64, Mips, Mips64, Msp430, PowerPc, PowerPc64, Riscv32, Riscv64, S390x, Sbf, Sharc, Sparc, Sparc32Plus, Sparc64, Wasm32, Wasm64, Xtensa, } /// A CPU sub-architecture. #[allow(missing_docs)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum SubArchitecture { Arm64E, Arm64EC, } impl Architecture { /// The size of an address value for this architecture. /// /// Returns `None` for unknown architectures. pub fn address_size(self) -> Option { match self { Architecture::Unknown => None, Architecture::Aarch64 => Some(AddressSize::U64), Architecture::Aarch64_Ilp32 => Some(AddressSize::U32), Architecture::Arm => Some(AddressSize::U32), Architecture::Avr => Some(AddressSize::U8), Architecture::Bpf => Some(AddressSize::U64), Architecture::Csky => Some(AddressSize::U32), Architecture::E2K32 => Some(AddressSize::U32), Architecture::E2K64 => Some(AddressSize::U64), Architecture::I386 => Some(AddressSize::U32), Architecture::X86_64 => Some(AddressSize::U64), Architecture::X86_64_X32 => Some(AddressSize::U32), Architecture::Hexagon => Some(AddressSize::U32), Architecture::LoongArch64 => Some(AddressSize::U64), Architecture::Mips => Some(AddressSize::U32), Architecture::Mips64 => Some(AddressSize::U64), Architecture::Msp430 => Some(AddressSize::U16), Architecture::PowerPc => Some(AddressSize::U32), Architecture::PowerPc64 => Some(AddressSize::U64), Architecture::Riscv32 => Some(AddressSize::U32), Architecture::Riscv64 => Some(AddressSize::U64), Architecture::S390x => Some(AddressSize::U64), Architecture::Sbf => Some(AddressSize::U64), Architecture::Sharc => Some(AddressSize::U32), Architecture::Sparc => Some(AddressSize::U32), Architecture::Sparc32Plus => Some(AddressSize::U32), Architecture::Sparc64 => Some(AddressSize::U64), Architecture::Wasm32 => Some(AddressSize::U32), Architecture::Wasm64 => Some(AddressSize::U64), Architecture::Xtensa => Some(AddressSize::U32), } } } /// The size of an address value for an architecture. /// /// This may differ from the address size supported by the file format (such as for COFF). #[allow(missing_docs)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] #[repr(u8)] pub enum AddressSize { U8 = 1, U16 = 2, U32 = 4, U64 = 8, } impl AddressSize { /// The size in bytes of an address value. #[inline] pub fn bytes(self) -> u8 { self as u8 } } /// A binary file format. #[allow(missing_docs)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum BinaryFormat { Coff, Elf, MachO, Pe, Wasm, Xcoff, } impl BinaryFormat { /// The target's native binary format for relocatable object files. /// /// Defaults to `Elf` for unknown platforms. pub fn native_object() -> BinaryFormat { if cfg!(target_os = "windows") { BinaryFormat::Coff } else if cfg!(target_os = "macos") { BinaryFormat::MachO } else { BinaryFormat::Elf } } } /// The kind of a section. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum SectionKind { /// The section kind is unknown. Unknown, /// An executable code section. /// /// Example ELF sections: `.text` /// /// Example Mach-O sections: `__TEXT/__text` Text, /// A data section. /// /// Example ELF sections: `.data` /// /// Example Mach-O sections: `__DATA/__data` Data, /// A read only data section. /// /// Example ELF sections: `.rodata` /// /// Example Mach-O sections: `__TEXT/__const`, `__DATA/__const`, `__TEXT/__literal4` ReadOnlyData, /// A read only data section with relocations. /// /// This is the same as either `Data` or `ReadOnlyData`, depending on the file format. /// This value is only used in the API for writing files. It is never returned when reading files. ReadOnlyDataWithRel, /// A loadable string section. /// /// Example ELF sections: `.rodata.str` /// /// Example Mach-O sections: `__TEXT/__cstring` ReadOnlyString, /// An uninitialized data section. /// /// Example ELF sections: `.bss` /// /// Example Mach-O sections: `__DATA/__bss` UninitializedData, /// An uninitialized common data section. /// /// Example Mach-O sections: `__DATA/__common` Common, /// A TLS data section. /// /// Example ELF sections: `.tdata` /// /// Example Mach-O sections: `__DATA/__thread_data` Tls, /// An uninitialized TLS data section. /// /// Example ELF sections: `.tbss` /// /// Example Mach-O sections: `__DATA/__thread_bss` UninitializedTls, /// A TLS variables section. /// /// This contains TLS variable structures, rather than the variable initializers. /// /// Example Mach-O sections: `__DATA/__thread_vars` TlsVariables, /// A non-loadable string section. /// /// Example ELF sections: `.comment`, `.debug_str` OtherString, /// Some other non-loadable section. /// /// Example ELF sections: `.debug_info` Other, /// Debug information. /// /// Example Mach-O sections: `__DWARF/__debug_info` Debug, /// Debug strings. /// /// This is the same as either `Debug` or `OtherString`, depending on the file format. /// This value is only used in the API for writing files. It is never returned when reading files. DebugString, /// Information for the linker. /// /// Example COFF sections: `.drectve` Linker, /// ELF note section. Note, /// Metadata such as symbols or relocations. /// /// Example ELF sections: `.symtab`, `.strtab`, `.group` Metadata, /// Some other ELF section type. /// /// This is the `sh_type` field in the section header. /// The meaning may be dependent on the architecture. Elf(u32), } impl SectionKind { /// Return true if this section contains zerofill data. pub fn is_bss(self) -> bool { self == SectionKind::UninitializedData || self == SectionKind::UninitializedTls || self == SectionKind::Common } } /// The selection kind for a COMDAT section group. /// /// This determines the way in which the linker resolves multiple definitions of the COMDAT /// sections. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum ComdatKind { /// The selection kind is unknown. Unknown, /// Multiple definitions are allowed. /// /// An arbitrary definition is selected, and the rest are removed. /// /// This is the only supported selection kind for ELF. Any, /// Multiple definitions are not allowed. /// /// This is used to group sections without allowing duplicates. NoDuplicates, /// Multiple definitions must have the same size. /// /// An arbitrary definition is selected, and the rest are removed. SameSize, /// Multiple definitions must match exactly. /// /// An arbitrary definition is selected, and the rest are removed. ExactMatch, /// Multiple definitions are allowed, and the largest is selected. /// /// An arbitrary definition with the largest size is selected, and the rest are removed. Largest, /// Multiple definitions are allowed, and the newest is selected. Newest, } /// The kind of a symbol. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum SymbolKind { /// The symbol kind is unknown. Unknown, /// The symbol is for executable code. Text, /// The symbol is for a data object. Data, /// The symbol is for a section. Section, /// The symbol is the name of a file. It precedes symbols within that file. File, /// The symbol is for a code label. Label, /// The symbol is for a thread local storage entity. Tls, } /// A symbol scope. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum SymbolScope { /// Unknown scope. Unknown, /// Symbol is visible to the compilation unit. Compilation, /// Symbol is visible to the static linkage unit. Linkage, /// Symbol is visible to dynamically linked objects. Dynamic, } /// The operation used to calculate the result of the relocation. /// /// The relocation descriptions use the following definitions. Note that /// these definitions probably don't match any ELF ABI. /// /// * A - The value of the addend. /// * G - The address of the symbol's entry within the global offset table. /// * L - The address of the symbol's entry within the procedure linkage table. /// * P - The address of the place of the relocation. /// * S - The address of the symbol. /// * GotBase - The address of the global offset table. /// * Image - The base address of the image. /// * Section - The address of the section containing the symbol. /// /// 'XxxRelative' means 'Xxx + A - P'. 'XxxOffset' means 'S + A - Xxx'. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum RelocationKind { /// The operation is unknown. Unknown, /// S + A Absolute, /// S + A - P Relative, /// G + A - GotBase Got, /// G + A - P GotRelative, /// GotBase + A - P GotBaseRelative, /// S + A - GotBase GotBaseOffset, /// L + A - P PltRelative, /// S + A - Image ImageOffset, /// S + A - Section SectionOffset, /// The index of the section containing the symbol. SectionIndex, } /// Information about how the result of the relocation operation is encoded in the place. /// /// This is usually architecture specific, such as specifying an addressing mode or /// a specific instruction. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum RelocationEncoding { /// The relocation encoding is unknown. Unknown, /// Generic encoding. Generic, /// x86 sign extension at runtime. /// /// Used with `RelocationKind::Absolute`. X86Signed, /// x86 rip-relative addressing. /// /// The `RelocationKind` must be PC relative. X86RipRelative, /// x86 rip-relative addressing in movq instruction. /// /// The `RelocationKind` must be PC relative. X86RipRelativeMovq, /// x86 branch instruction. /// /// The `RelocationKind` must be PC relative. X86Branch, /// s390x PC-relative offset shifted right by one bit. /// /// The `RelocationKind` must be PC relative. S390xDbl, /// AArch64 call target. /// /// The `RelocationKind` must be PC relative. AArch64Call, /// LoongArch branch offset with two trailing zeros. /// /// The `RelocationKind` must be PC relative. LoongArchBranch, /// SHARC+ 48-bit Type A instruction /// /// Represents these possible variants, each with a corresponding /// `R_SHARC_*` constant: /// /// * 24-bit absolute address /// * 32-bit absolute address /// * 6-bit relative address /// * 24-bit relative address /// * 6-bit absolute address in the immediate value field /// * 16-bit absolute address in the immediate value field SharcTypeA, /// SHARC+ 32-bit Type B instruction /// /// Represents these possible variants, each with a corresponding /// `R_SHARC_*` constant: /// /// * 6-bit absolute address in the immediate value field /// * 7-bit absolute address in the immediate value field /// * 16-bit absolute address /// * 6-bit relative address SharcTypeB, /// E2K 64-bit value stored in two LTS /// /// Memory representation: /// ```text /// 0: LTS1 = value[63:32] /// 4: LTS0 = value[31:0] /// ``` E2KLit, /// E2K 28-bit value stored in CS0 E2KDisp, } /// File flags that are specific to each file format. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum FileFlags { /// No file flags. None, /// ELF file flags. Elf { /// `os_abi` field in the ELF file header. os_abi: u8, /// `abi_version` field in the ELF file header. abi_version: u8, /// `e_flags` field in the ELF file header. e_flags: u32, }, /// Mach-O file flags. MachO { /// `flags` field in the Mach-O file header. flags: u32, }, /// COFF file flags. Coff { /// `Characteristics` field in the COFF file header. characteristics: u16, }, /// XCOFF file flags. Xcoff { /// `f_flags` field in the XCOFF file header. f_flags: u16, }, } /// Segment flags that are specific to each file format. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum SegmentFlags { /// No segment flags. None, /// ELF segment flags. Elf { /// `p_flags` field in the segment header. p_flags: u32, }, /// Mach-O segment flags. MachO { /// `flags` field in the segment header. flags: u32, /// `maxprot` field in the segment header. maxprot: u32, /// `initprot` field in the segment header. initprot: u32, }, /// COFF segment flags. Coff { /// `Characteristics` field in the segment header. characteristics: u32, }, } /// Section flags that are specific to each file format. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum SectionFlags { /// No section flags. None, /// ELF section flags. Elf { /// `sh_flags` field in the section header. sh_flags: u64, }, /// Mach-O section flags. MachO { /// `flags` field in the section header. flags: u32, }, /// COFF section flags. Coff { /// `Characteristics` field in the section header. characteristics: u32, }, /// XCOFF section flags. Xcoff { /// `s_flags` field in the section header. s_flags: u32, }, } /// Symbol flags that are specific to each file format. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum SymbolFlags { /// No symbol flags. None, /// ELF symbol flags. Elf { /// `st_info` field in the ELF symbol. st_info: u8, /// `st_other` field in the ELF symbol. st_other: u8, }, /// Mach-O symbol flags. MachO { /// `n_desc` field in the Mach-O symbol. n_desc: u16, }, /// COFF flags for a section symbol. CoffSection { /// `Selection` field in the auxiliary symbol for the section. selection: u8, /// `Number` field in the auxiliary symbol for the section. associative_section: Option
, }, /// XCOFF symbol flags. Xcoff { /// `n_sclass` field in the XCOFF symbol. n_sclass: u8, /// `x_smtyp` field in the CSECT auxiliary symbol. /// /// Only valid if `n_sclass` is `C_EXT`, `C_WEAKEXT`, or `C_HIDEXT`. x_smtyp: u8, /// `x_smclas` field in the CSECT auxiliary symbol. /// /// Only valid if `n_sclass` is `C_EXT`, `C_WEAKEXT`, or `C_HIDEXT`. x_smclas: u8, /// The containing csect for the symbol. /// /// Only valid if `x_smtyp` is `XTY_LD`. containing_csect: Option, }, } /// Relocation fields that are specific to each file format and architecture. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum RelocationFlags { /// Format independent representation. Generic { /// The operation used to calculate the result of the relocation. kind: RelocationKind, /// Information about how the result of the relocation operation is encoded in the place. encoding: RelocationEncoding, /// The size in bits of the place of relocation. size: u8, }, /// ELF relocation fields. Elf { /// `r_type` field in the ELF relocation. r_type: u32, }, /// Mach-O relocation fields. MachO { /// `r_type` field in the Mach-O relocation. r_type: u8, /// `r_pcrel` field in the Mach-O relocation. r_pcrel: bool, /// `r_length` field in the Mach-O relocation. r_length: u8, }, /// COFF relocation fields. Coff { /// `typ` field in the COFF relocation. typ: u16, }, /// XCOFF relocation fields. Xcoff { /// `r_rtype` field in the XCOFF relocation. r_rtype: u8, /// `r_rsize` field in the XCOFF relocation. r_rsize: u8, }, } object-0.36.5/src/elf.rs000064400000000000000000006563301046102023000131050ustar 00000000000000//! ELF definitions. //! //! These definitions are independent of read/write support, although we do implement //! some traits useful for those. //! //! This module is the equivalent of /usr/include/elf.h, and is based heavily on it. #![allow(missing_docs)] #![allow(clippy::identity_op)] use crate::endian::{Endian, U32Bytes, U64Bytes, I32, I64, U16, U32, U64}; use crate::pod::Pod; /// The header at the start of every 32-bit ELF file. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct FileHeader32 { /// Magic number and other information. pub e_ident: Ident, /// Object file type. One of the `ET_*` constants. pub e_type: U16, /// Architecture. One of the `EM_*` constants. pub e_machine: U16, /// Object file version. Must be `EV_CURRENT`. pub e_version: U32, /// Entry point virtual address. pub e_entry: U32, /// Program header table file offset. pub e_phoff: U32, /// Section header table file offset. pub e_shoff: U32, /// Processor-specific flags. /// /// A combination of the `EF_*` constants. pub e_flags: U32, /// Size in bytes of this header. pub e_ehsize: U16, /// Program header table entry size. pub e_phentsize: U16, /// Program header table entry count. /// /// If the count is greater than or equal to `PN_XNUM` then this field is set to /// `PN_XNUM` and the count is stored in the `sh_info` field of section 0. pub e_phnum: U16, /// Section header table entry size. pub e_shentsize: U16, /// Section header table entry count. /// /// If the count is greater than or equal to `SHN_LORESERVE` then this field is set to /// `0` and the count is stored in the `sh_size` field of section 0. /// first section header. pub e_shnum: U16, /// Section header string table index. /// /// If the index is greater than or equal to `SHN_LORESERVE` then this field is set to /// `SHN_XINDEX` and the index is stored in the `sh_link` field of section 0. pub e_shstrndx: U16, } /// The header at the start of every 64-bit ELF file. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct FileHeader64 { /// Magic number and other information. pub e_ident: Ident, /// Object file type. One of the `ET_*` constants. pub e_type: U16, /// Architecture. One of the `EM_*` constants. pub e_machine: U16, /// Object file version. Must be `EV_CURRENT`. pub e_version: U32, /// Entry point virtual address. pub e_entry: U64, /// Program header table file offset. pub e_phoff: U64, /// Section header table file offset. pub e_shoff: U64, /// Processor-specific flags. /// /// A combination of the `EF_*` constants. pub e_flags: U32, /// Size in bytes of this header. pub e_ehsize: U16, /// Program header table entry size. pub e_phentsize: U16, /// Program header table entry count. /// /// If the count is greater than or equal to `PN_XNUM` then this field is set to /// `PN_XNUM` and the count is stored in the `sh_info` field of section 0. pub e_phnum: U16, /// Section header table entry size. pub e_shentsize: U16, /// Section header table entry count. /// /// If the count is greater than or equal to `SHN_LORESERVE` then this field is set to /// `0` and the count is stored in the `sh_size` field of section 0. /// first section header. pub e_shnum: U16, /// Section header string table index. /// /// If the index is greater than or equal to `SHN_LORESERVE` then this field is set to /// `SHN_XINDEX` and the index is stored in the `sh_link` field of section 0. pub e_shstrndx: U16, } /// Magic number and other information. /// /// Contained in the file header. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Ident { /// Magic number. Must be `ELFMAG`. pub magic: [u8; 4], /// File class. One of the `ELFCLASS*` constants. pub class: u8, /// Data encoding. One of the `ELFDATA*` constants. pub data: u8, /// ELF version. Must be `EV_CURRENT`. pub version: u8, /// OS ABI identification. One of the `ELFOSABI*` constants. pub os_abi: u8, /// ABI version. /// /// The meaning of this field depends on the `os_abi` value. pub abi_version: u8, /// Padding bytes. pub padding: [u8; 7], } /// File identification bytes stored in `Ident::magic`. pub const ELFMAG: [u8; 4] = [0x7f, b'E', b'L', b'F']; // Values for `Ident::class`. /// Invalid class. pub const ELFCLASSNONE: u8 = 0; /// 32-bit object. pub const ELFCLASS32: u8 = 1; /// 64-bit object. pub const ELFCLASS64: u8 = 2; // Values for `Ident::data`. /// Invalid data encoding. pub const ELFDATANONE: u8 = 0; /// 2's complement, little endian. pub const ELFDATA2LSB: u8 = 1; /// 2's complement, big endian. pub const ELFDATA2MSB: u8 = 2; // Values for `Ident::os_abi`. /// UNIX System V ABI. pub const ELFOSABI_NONE: u8 = 0; /// UNIX System V ABI. /// /// Alias. pub const ELFOSABI_SYSV: u8 = 0; /// HP-UX. pub const ELFOSABI_HPUX: u8 = 1; /// NetBSD. pub const ELFOSABI_NETBSD: u8 = 2; /// Object uses GNU ELF extensions. pub const ELFOSABI_GNU: u8 = 3; /// Object uses GNU ELF extensions. /// /// Compatibility alias. pub const ELFOSABI_LINUX: u8 = ELFOSABI_GNU; /// GNU/Hurd. pub const ELFOSABI_HURD: u8 = 4; /// Sun Solaris. pub const ELFOSABI_SOLARIS: u8 = 6; /// IBM AIX. pub const ELFOSABI_AIX: u8 = 7; /// SGI Irix. pub const ELFOSABI_IRIX: u8 = 8; /// FreeBSD. pub const ELFOSABI_FREEBSD: u8 = 9; /// Compaq TRU64 UNIX. pub const ELFOSABI_TRU64: u8 = 10; /// Novell Modesto. pub const ELFOSABI_MODESTO: u8 = 11; /// OpenBSD. pub const ELFOSABI_OPENBSD: u8 = 12; /// OpenVMS. pub const ELFOSABI_OPENVMS: u8 = 13; /// Hewlett-Packard Non-Stop Kernel. pub const ELFOSABI_NSK: u8 = 14; /// AROS pub const ELFOSABI_AROS: u8 = 15; /// FenixOS pub const ELFOSABI_FENIXOS: u8 = 16; /// Nuxi CloudABI pub const ELFOSABI_CLOUDABI: u8 = 17; /// ARM EABI. pub const ELFOSABI_ARM_AEABI: u8 = 64; /// ARM. pub const ELFOSABI_ARM: u8 = 97; /// Standalone (embedded) application. pub const ELFOSABI_STANDALONE: u8 = 255; // Values for `FileHeader*::e_type`. /// No file type. pub const ET_NONE: u16 = 0; /// Relocatable file. pub const ET_REL: u16 = 1; /// Executable file. pub const ET_EXEC: u16 = 2; /// Shared object file. pub const ET_DYN: u16 = 3; /// Core file. pub const ET_CORE: u16 = 4; /// OS-specific range start. pub const ET_LOOS: u16 = 0xfe00; /// OS-specific range end. pub const ET_HIOS: u16 = 0xfeff; /// Processor-specific range start. pub const ET_LOPROC: u16 = 0xff00; /// Processor-specific range end. pub const ET_HIPROC: u16 = 0xffff; // Values for `FileHeader*::e_machine`. /// No machine pub const EM_NONE: u16 = 0; /// AT&T WE 32100 pub const EM_M32: u16 = 1; /// SUN SPARC pub const EM_SPARC: u16 = 2; /// Intel 80386 pub const EM_386: u16 = 3; /// Motorola m68k family pub const EM_68K: u16 = 4; /// Motorola m88k family pub const EM_88K: u16 = 5; /// Intel MCU pub const EM_IAMCU: u16 = 6; /// Intel 80860 pub const EM_860: u16 = 7; /// MIPS R3000 big-endian pub const EM_MIPS: u16 = 8; /// IBM System/370 pub const EM_S370: u16 = 9; /// MIPS R3000 little-endian pub const EM_MIPS_RS3_LE: u16 = 10; /// HPPA pub const EM_PARISC: u16 = 15; /// Fujitsu VPP500 pub const EM_VPP500: u16 = 17; /// Sun's "v8plus" pub const EM_SPARC32PLUS: u16 = 18; /// Intel 80960 pub const EM_960: u16 = 19; /// PowerPC pub const EM_PPC: u16 = 20; /// PowerPC 64-bit pub const EM_PPC64: u16 = 21; /// IBM S390 pub const EM_S390: u16 = 22; /// IBM SPU/SPC pub const EM_SPU: u16 = 23; /// NEC V800 series pub const EM_V800: u16 = 36; /// Fujitsu FR20 pub const EM_FR20: u16 = 37; /// TRW RH-32 pub const EM_RH32: u16 = 38; /// Motorola RCE pub const EM_RCE: u16 = 39; /// ARM pub const EM_ARM: u16 = 40; /// Digital Alpha pub const EM_FAKE_ALPHA: u16 = 41; /// Hitachi SH pub const EM_SH: u16 = 42; /// SPARC v9 64-bit pub const EM_SPARCV9: u16 = 43; /// Siemens Tricore pub const EM_TRICORE: u16 = 44; /// Argonaut RISC Core pub const EM_ARC: u16 = 45; /// Hitachi H8/300 pub const EM_H8_300: u16 = 46; /// Hitachi H8/300H pub const EM_H8_300H: u16 = 47; /// Hitachi H8S pub const EM_H8S: u16 = 48; /// Hitachi H8/500 pub const EM_H8_500: u16 = 49; /// Intel Merced pub const EM_IA_64: u16 = 50; /// Stanford MIPS-X pub const EM_MIPS_X: u16 = 51; /// Motorola Coldfire pub const EM_COLDFIRE: u16 = 52; /// Motorola M68HC12 pub const EM_68HC12: u16 = 53; /// Fujitsu MMA Multimedia Accelerator pub const EM_MMA: u16 = 54; /// Siemens PCP pub const EM_PCP: u16 = 55; /// Sony nCPU embeeded RISC pub const EM_NCPU: u16 = 56; /// Denso NDR1 microprocessor pub const EM_NDR1: u16 = 57; /// Motorola Start*Core processor pub const EM_STARCORE: u16 = 58; /// Toyota ME16 processor pub const EM_ME16: u16 = 59; /// STMicroelectronic ST100 processor pub const EM_ST100: u16 = 60; /// Advanced Logic Corp. Tinyj emb.fam pub const EM_TINYJ: u16 = 61; /// AMD x86-64 architecture pub const EM_X86_64: u16 = 62; /// Sony DSP Processor pub const EM_PDSP: u16 = 63; /// Digital PDP-10 pub const EM_PDP10: u16 = 64; /// Digital PDP-11 pub const EM_PDP11: u16 = 65; /// Siemens FX66 microcontroller pub const EM_FX66: u16 = 66; /// STMicroelectronics ST9+ 8/16 mc pub const EM_ST9PLUS: u16 = 67; /// STmicroelectronics ST7 8 bit mc pub const EM_ST7: u16 = 68; /// Motorola MC68HC16 microcontroller pub const EM_68HC16: u16 = 69; /// Motorola MC68HC11 microcontroller pub const EM_68HC11: u16 = 70; /// Motorola MC68HC08 microcontroller pub const EM_68HC08: u16 = 71; /// Motorola MC68HC05 microcontroller pub const EM_68HC05: u16 = 72; /// Silicon Graphics SVx pub const EM_SVX: u16 = 73; /// STMicroelectronics ST19 8 bit mc pub const EM_ST19: u16 = 74; /// Digital VAX pub const EM_VAX: u16 = 75; /// Axis Communications 32-bit emb.proc pub const EM_CRIS: u16 = 76; /// Infineon Technologies 32-bit emb.proc pub const EM_JAVELIN: u16 = 77; /// Element 14 64-bit DSP Processor pub const EM_FIREPATH: u16 = 78; /// LSI Logic 16-bit DSP Processor pub const EM_ZSP: u16 = 79; /// Donald Knuth's educational 64-bit proc pub const EM_MMIX: u16 = 80; /// Harvard University machine-independent object files pub const EM_HUANY: u16 = 81; /// SiTera Prism pub const EM_PRISM: u16 = 82; /// Atmel AVR 8-bit microcontroller pub const EM_AVR: u16 = 83; /// Fujitsu FR30 pub const EM_FR30: u16 = 84; /// Mitsubishi D10V pub const EM_D10V: u16 = 85; /// Mitsubishi D30V pub const EM_D30V: u16 = 86; /// NEC v850 pub const EM_V850: u16 = 87; /// Mitsubishi M32R pub const EM_M32R: u16 = 88; /// Matsushita MN10300 pub const EM_MN10300: u16 = 89; /// Matsushita MN10200 pub const EM_MN10200: u16 = 90; /// picoJava pub const EM_PJ: u16 = 91; /// OpenRISC 32-bit embedded processor pub const EM_OPENRISC: u16 = 92; /// ARC International ARCompact pub const EM_ARC_COMPACT: u16 = 93; /// Tensilica Xtensa Architecture pub const EM_XTENSA: u16 = 94; /// Alphamosaic VideoCore pub const EM_VIDEOCORE: u16 = 95; /// Thompson Multimedia General Purpose Proc pub const EM_TMM_GPP: u16 = 96; /// National Semi. 32000 pub const EM_NS32K: u16 = 97; /// Tenor Network TPC pub const EM_TPC: u16 = 98; /// Trebia SNP 1000 pub const EM_SNP1K: u16 = 99; /// STMicroelectronics ST200 pub const EM_ST200: u16 = 100; /// Ubicom IP2xxx pub const EM_IP2K: u16 = 101; /// MAX processor pub const EM_MAX: u16 = 102; /// National Semi. CompactRISC pub const EM_CR: u16 = 103; /// Fujitsu F2MC16 pub const EM_F2MC16: u16 = 104; /// Texas Instruments msp430 pub const EM_MSP430: u16 = 105; /// Analog Devices Blackfin DSP pub const EM_BLACKFIN: u16 = 106; /// Seiko Epson S1C33 family pub const EM_SE_C33: u16 = 107; /// Sharp embedded microprocessor pub const EM_SEP: u16 = 108; /// Arca RISC pub const EM_ARCA: u16 = 109; /// PKU-Unity & MPRC Peking Uni. mc series pub const EM_UNICORE: u16 = 110; /// eXcess configurable cpu pub const EM_EXCESS: u16 = 111; /// Icera Semi. Deep Execution Processor pub const EM_DXP: u16 = 112; /// Altera Nios II pub const EM_ALTERA_NIOS2: u16 = 113; /// National Semi. CompactRISC CRX pub const EM_CRX: u16 = 114; /// Motorola XGATE pub const EM_XGATE: u16 = 115; /// Infineon C16x/XC16x pub const EM_C166: u16 = 116; /// Renesas M16C pub const EM_M16C: u16 = 117; /// Microchip Technology dsPIC30F pub const EM_DSPIC30F: u16 = 118; /// Freescale Communication Engine RISC pub const EM_CE: u16 = 119; /// Renesas M32C pub const EM_M32C: u16 = 120; /// Altium TSK3000 pub const EM_TSK3000: u16 = 131; /// Freescale RS08 pub const EM_RS08: u16 = 132; /// Analog Devices SHARC family pub const EM_SHARC: u16 = 133; /// Cyan Technology eCOG2 pub const EM_ECOG2: u16 = 134; /// Sunplus S+core7 RISC pub const EM_SCORE7: u16 = 135; /// New Japan Radio (NJR) 24-bit DSP pub const EM_DSP24: u16 = 136; /// Broadcom VideoCore III pub const EM_VIDEOCORE3: u16 = 137; /// RISC for Lattice FPGA pub const EM_LATTICEMICO32: u16 = 138; /// Seiko Epson C17 pub const EM_SE_C17: u16 = 139; /// Texas Instruments TMS320C6000 DSP pub const EM_TI_C6000: u16 = 140; /// Texas Instruments TMS320C2000 DSP pub const EM_TI_C2000: u16 = 141; /// Texas Instruments TMS320C55x DSP pub const EM_TI_C5500: u16 = 142; /// Texas Instruments App. Specific RISC pub const EM_TI_ARP32: u16 = 143; /// Texas Instruments Prog. Realtime Unit pub const EM_TI_PRU: u16 = 144; /// STMicroelectronics 64bit VLIW DSP pub const EM_MMDSP_PLUS: u16 = 160; /// Cypress M8C pub const EM_CYPRESS_M8C: u16 = 161; /// Renesas R32C pub const EM_R32C: u16 = 162; /// NXP Semi. TriMedia pub const EM_TRIMEDIA: u16 = 163; /// QUALCOMM Hexagon pub const EM_HEXAGON: u16 = 164; /// Intel 8051 and variants pub const EM_8051: u16 = 165; /// STMicroelectronics STxP7x pub const EM_STXP7X: u16 = 166; /// Andes Tech. compact code emb. RISC pub const EM_NDS32: u16 = 167; /// Cyan Technology eCOG1X pub const EM_ECOG1X: u16 = 168; /// Dallas Semi. MAXQ30 mc pub const EM_MAXQ30: u16 = 169; /// New Japan Radio (NJR) 16-bit DSP pub const EM_XIMO16: u16 = 170; /// M2000 Reconfigurable RISC pub const EM_MANIK: u16 = 171; /// Cray NV2 vector architecture pub const EM_CRAYNV2: u16 = 172; /// Renesas RX pub const EM_RX: u16 = 173; /// Imagination Tech. META pub const EM_METAG: u16 = 174; /// MCST Elbrus pub const EM_MCST_ELBRUS: u16 = 175; /// Cyan Technology eCOG16 pub const EM_ECOG16: u16 = 176; /// National Semi. CompactRISC CR16 pub const EM_CR16: u16 = 177; /// Freescale Extended Time Processing Unit pub const EM_ETPU: u16 = 178; /// Infineon Tech. SLE9X pub const EM_SLE9X: u16 = 179; /// Intel L10M pub const EM_L10M: u16 = 180; /// Intel K10M pub const EM_K10M: u16 = 181; /// ARM AARCH64 pub const EM_AARCH64: u16 = 183; /// Amtel 32-bit microprocessor pub const EM_AVR32: u16 = 185; /// STMicroelectronics STM8 pub const EM_STM8: u16 = 186; /// Tileta TILE64 pub const EM_TILE64: u16 = 187; /// Tilera TILEPro pub const EM_TILEPRO: u16 = 188; /// Xilinx MicroBlaze pub const EM_MICROBLAZE: u16 = 189; /// NVIDIA CUDA pub const EM_CUDA: u16 = 190; /// Tilera TILE-Gx pub const EM_TILEGX: u16 = 191; /// CloudShield pub const EM_CLOUDSHIELD: u16 = 192; /// KIPO-KAIST Core-A 1st gen. pub const EM_COREA_1ST: u16 = 193; /// KIPO-KAIST Core-A 2nd gen. pub const EM_COREA_2ND: u16 = 194; /// Synopsys ARCompact V2 pub const EM_ARC_COMPACT2: u16 = 195; /// Open8 RISC pub const EM_OPEN8: u16 = 196; /// Renesas RL78 pub const EM_RL78: u16 = 197; /// Broadcom VideoCore V pub const EM_VIDEOCORE5: u16 = 198; /// Renesas 78KOR pub const EM_78KOR: u16 = 199; /// Freescale 56800EX DSC pub const EM_56800EX: u16 = 200; /// Beyond BA1 pub const EM_BA1: u16 = 201; /// Beyond BA2 pub const EM_BA2: u16 = 202; /// XMOS xCORE pub const EM_XCORE: u16 = 203; /// Microchip 8-bit PIC(r) pub const EM_MCHP_PIC: u16 = 204; /// KM211 KM32 pub const EM_KM32: u16 = 210; /// KM211 KMX32 pub const EM_KMX32: u16 = 211; /// KM211 KMX16 pub const EM_EMX16: u16 = 212; /// KM211 KMX8 pub const EM_EMX8: u16 = 213; /// KM211 KVARC pub const EM_KVARC: u16 = 214; /// Paneve CDP pub const EM_CDP: u16 = 215; /// Cognitive Smart Memory Processor pub const EM_COGE: u16 = 216; /// Bluechip CoolEngine pub const EM_COOL: u16 = 217; /// Nanoradio Optimized RISC pub const EM_NORC: u16 = 218; /// CSR Kalimba pub const EM_CSR_KALIMBA: u16 = 219; /// Zilog Z80 pub const EM_Z80: u16 = 220; /// Controls and Data Services VISIUMcore pub const EM_VISIUM: u16 = 221; /// FTDI Chip FT32 pub const EM_FT32: u16 = 222; /// Moxie processor pub const EM_MOXIE: u16 = 223; /// AMD GPU pub const EM_AMDGPU: u16 = 224; /// RISC-V pub const EM_RISCV: u16 = 243; /// Linux BPF -- in-kernel virtual machine pub const EM_BPF: u16 = 247; /// C-SKY pub const EM_CSKY: u16 = 252; /// Loongson LoongArch pub const EM_LOONGARCH: u16 = 258; /// Solana Binary Format pub const EM_SBF: u16 = 263; /// Digital Alpha pub const EM_ALPHA: u16 = 0x9026; // Values for `FileHeader*::e_version` and `Ident::version`. /// Invalid ELF version. pub const EV_NONE: u8 = 0; /// Current ELF version. pub const EV_CURRENT: u8 = 1; /// Section header. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct SectionHeader32 { /// Section name. /// /// This is an offset into the section header string table. pub sh_name: U32, /// Section type. One of the `SHT_*` constants. pub sh_type: U32, /// Section flags. A combination of the `SHF_*` constants. pub sh_flags: U32, /// Section virtual address at execution. pub sh_addr: U32, /// Section file offset. pub sh_offset: U32, /// Section size in bytes. pub sh_size: U32, /// Link to another section. /// /// The section relationship depends on the `sh_type` value. pub sh_link: U32, /// Additional section information. /// /// The meaning of this field depends on the `sh_type` value. pub sh_info: U32, /// Section alignment. pub sh_addralign: U32, /// Entry size if the section holds a table. pub sh_entsize: U32, } /// Section header. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct SectionHeader64 { /// Section name. /// /// This is an offset into the section header string table. pub sh_name: U32, /// Section type. One of the `SHT_*` constants. pub sh_type: U32, /// Section flags. A combination of the `SHF_*` constants. pub sh_flags: U64, /// Section virtual address at execution. pub sh_addr: U64, /// Section file offset. pub sh_offset: U64, /// Section size in bytes. pub sh_size: U64, /// Link to another section. /// /// The section relationship depends on the `sh_type` value. pub sh_link: U32, /// Additional section information. /// /// The meaning of this field depends on the `sh_type` value. pub sh_info: U32, /// Section alignment. pub sh_addralign: U64, /// Entry size if the section holds a table. pub sh_entsize: U64, } // Special values for section indices. /// Undefined section. pub const SHN_UNDEF: u16 = 0; /// OS-specific range start. /// Start of reserved section indices. pub const SHN_LORESERVE: u16 = 0xff00; /// Start of processor-specific section indices. pub const SHN_LOPROC: u16 = 0xff00; /// End of processor-specific section indices. pub const SHN_HIPROC: u16 = 0xff1f; /// Start of OS-specific section indices. pub const SHN_LOOS: u16 = 0xff20; /// End of OS-specific section indices. pub const SHN_HIOS: u16 = 0xff3f; /// Associated symbol is absolute. pub const SHN_ABS: u16 = 0xfff1; /// Associated symbol is common. pub const SHN_COMMON: u16 = 0xfff2; /// Section index is in the `SHT_SYMTAB_SHNDX` section. pub const SHN_XINDEX: u16 = 0xffff; /// End of reserved section indices. pub const SHN_HIRESERVE: u16 = 0xffff; // Values for `SectionHeader*::sh_type`. /// Section header table entry is unused. pub const SHT_NULL: u32 = 0; /// Program data. pub const SHT_PROGBITS: u32 = 1; /// Symbol table. pub const SHT_SYMTAB: u32 = 2; /// String table. pub const SHT_STRTAB: u32 = 3; /// Relocation entries with explicit addends. pub const SHT_RELA: u32 = 4; /// Symbol hash table. pub const SHT_HASH: u32 = 5; /// Dynamic linking information. pub const SHT_DYNAMIC: u32 = 6; /// Notes. pub const SHT_NOTE: u32 = 7; /// Program space with no data (bss). pub const SHT_NOBITS: u32 = 8; /// Relocation entries without explicit addends. pub const SHT_REL: u32 = 9; /// Reserved section type. pub const SHT_SHLIB: u32 = 10; /// Dynamic linker symbol table. pub const SHT_DYNSYM: u32 = 11; /// Array of constructors. pub const SHT_INIT_ARRAY: u32 = 14; /// Array of destructors. pub const SHT_FINI_ARRAY: u32 = 15; /// Array of pre-constructors. pub const SHT_PREINIT_ARRAY: u32 = 16; /// Section group. pub const SHT_GROUP: u32 = 17; /// Extended section indices for a symbol table. pub const SHT_SYMTAB_SHNDX: u32 = 18; /// Start of OS-specific section types. pub const SHT_LOOS: u32 = 0x6000_0000; /// Object attributes. pub const SHT_GNU_ATTRIBUTES: u32 = 0x6fff_fff5; /// GNU-style hash table. pub const SHT_GNU_HASH: u32 = 0x6fff_fff6; /// Prelink library list pub const SHT_GNU_LIBLIST: u32 = 0x6fff_fff7; /// Checksum for DSO content. pub const SHT_CHECKSUM: u32 = 0x6fff_fff8; /// Sun-specific low bound. pub const SHT_LOSUNW: u32 = 0x6fff_fffa; #[allow(non_upper_case_globals)] pub const SHT_SUNW_move: u32 = 0x6fff_fffa; pub const SHT_SUNW_COMDAT: u32 = 0x6fff_fffb; #[allow(non_upper_case_globals)] pub const SHT_SUNW_syminfo: u32 = 0x6fff_fffc; /// Version definition section. #[allow(non_upper_case_globals)] pub const SHT_GNU_VERDEF: u32 = 0x6fff_fffd; /// Version needs section. #[allow(non_upper_case_globals)] pub const SHT_GNU_VERNEED: u32 = 0x6fff_fffe; /// Version symbol table. #[allow(non_upper_case_globals)] pub const SHT_GNU_VERSYM: u32 = 0x6fff_ffff; /// Sun-specific high bound. pub const SHT_HISUNW: u32 = 0x6fff_ffff; /// End of OS-specific section types. pub const SHT_HIOS: u32 = 0x6fff_ffff; /// Start of processor-specific section types. pub const SHT_LOPROC: u32 = 0x7000_0000; /// End of processor-specific section types. pub const SHT_HIPROC: u32 = 0x7fff_ffff; /// Start of application-specific section types. pub const SHT_LOUSER: u32 = 0x8000_0000; /// End of application-specific section types. pub const SHT_HIUSER: u32 = 0x8fff_ffff; // Values for `SectionHeader*::sh_flags`. /// Section is writable. pub const SHF_WRITE: u32 = 1 << 0; /// Section occupies memory during execution. pub const SHF_ALLOC: u32 = 1 << 1; /// Section is executable. pub const SHF_EXECINSTR: u32 = 1 << 2; /// Section may be be merged to eliminate duplication. pub const SHF_MERGE: u32 = 1 << 4; /// Section contains nul-terminated strings. pub const SHF_STRINGS: u32 = 1 << 5; /// The `sh_info` field contains a section header table index. pub const SHF_INFO_LINK: u32 = 1 << 6; /// Section has special ordering requirements when combining sections. pub const SHF_LINK_ORDER: u32 = 1 << 7; /// Section requires special OS-specific handling. pub const SHF_OS_NONCONFORMING: u32 = 1 << 8; /// Section is a member of a group. pub const SHF_GROUP: u32 = 1 << 9; /// Section holds thread-local storage. pub const SHF_TLS: u32 = 1 << 10; /// Section is compressed. /// /// Compressed sections begin with one of the `CompressionHeader*` headers. pub const SHF_COMPRESSED: u32 = 1 << 11; /// OS-specific section flags. pub const SHF_MASKOS: u32 = 0x0ff0_0000; /// Section should not be garbage collected by the linker. pub const SHF_GNU_RETAIN: u32 = 1 << 21; /// Mbind section. pub const SHF_GNU_MBIND: u32 = 1 << 24; /// Processor-specific section flags. pub const SHF_MASKPROC: u32 = 0xf000_0000; /// This section is excluded from the final executable or shared library. pub const SHF_EXCLUDE: u32 = 0x8000_0000; /// Section compression header. /// /// Used when `SHF_COMPRESSED` is set. /// /// Note: this type currently allows for misaligned headers, but that may be /// changed in a future version. #[derive(Debug, Default, Clone, Copy)] #[repr(C)] pub struct CompressionHeader32 { /// Compression format. One of the `ELFCOMPRESS_*` values. pub ch_type: U32Bytes, /// Uncompressed data size. pub ch_size: U32Bytes, /// Uncompressed data alignment. pub ch_addralign: U32Bytes, } /// Section compression header. /// /// Used when `SHF_COMPRESSED` is set. /// /// Note: this type currently allows for misaligned headers, but that may be /// changed in a future version. #[derive(Debug, Default, Clone, Copy)] #[repr(C)] pub struct CompressionHeader64 { /// Compression format. One of the `ELFCOMPRESS_*` values. pub ch_type: U32Bytes, /// Reserved. pub ch_reserved: U32Bytes, /// Uncompressed data size. pub ch_size: U64Bytes, /// Uncompressed data alignment. pub ch_addralign: U64Bytes, } /// ZLIB/DEFLATE algorithm. pub const ELFCOMPRESS_ZLIB: u32 = 1; /// Zstandard algorithm. pub const ELFCOMPRESS_ZSTD: u32 = 2; /// Start of OS-specific compression types. pub const ELFCOMPRESS_LOOS: u32 = 0x6000_0000; /// End of OS-specific compression types. pub const ELFCOMPRESS_HIOS: u32 = 0x6fff_ffff; /// Start of processor-specific compression types. pub const ELFCOMPRESS_LOPROC: u32 = 0x7000_0000; /// End of processor-specific compression types. pub const ELFCOMPRESS_HIPROC: u32 = 0x7fff_ffff; // Values for the flag entry for section groups. /// Mark group as COMDAT. pub const GRP_COMDAT: u32 = 1; /// Symbol table entry. #[derive(Debug, Default, Clone, Copy)] #[repr(C)] pub struct Sym32 { /// Symbol name. /// /// This is an offset into the symbol string table. pub st_name: U32, /// Symbol value. pub st_value: U32, /// Symbol size. pub st_size: U32, /// Symbol type and binding. /// /// Use the `st_type` and `st_bind` methods to access this value. pub st_info: u8, /// Symbol visibility. /// /// Use the `st_visibility` method to access this value. pub st_other: u8, /// Section index or one of the `SHN_*` values. pub st_shndx: U16, } impl Sym32 { /// Get the `st_bind` component of the `st_info` field. #[inline] pub fn st_bind(&self) -> u8 { self.st_info >> 4 } /// Get the `st_type` component of the `st_info` field. #[inline] pub fn st_type(&self) -> u8 { self.st_info & 0xf } /// Set the `st_info` field given the `st_bind` and `st_type` components. #[inline] pub fn set_st_info(&mut self, st_bind: u8, st_type: u8) { self.st_info = (st_bind << 4) + (st_type & 0xf); } /// Get the `st_visibility` component of the `st_info` field. #[inline] pub fn st_visibility(&self) -> u8 { self.st_other & 0x3 } } /// Symbol table entry. #[derive(Debug, Default, Clone, Copy)] #[repr(C)] pub struct Sym64 { /// Symbol name. /// /// This is an offset into the symbol string table. pub st_name: U32, /// Symbol type and binding. /// /// Use the `st_bind` and `st_type` methods to access this value. pub st_info: u8, /// Symbol visibility. /// /// Use the `st_visibility` method to access this value. pub st_other: u8, /// Section index or one of the `SHN_*` values. pub st_shndx: U16, /// Symbol value. pub st_value: U64, /// Symbol size. pub st_size: U64, } impl Sym64 { /// Get the `st_bind` component of the `st_info` field. #[inline] pub fn st_bind(&self) -> u8 { self.st_info >> 4 } /// Get the `st_type` component of the `st_info` field. #[inline] pub fn st_type(&self) -> u8 { self.st_info & 0xf } /// Set the `st_info` field given the `st_bind` and `st_type` components. #[inline] pub fn set_st_info(&mut self, st_bind: u8, st_type: u8) { self.st_info = (st_bind << 4) + (st_type & 0xf); } /// Get the `st_visibility` component of the `st_info` field. #[inline] pub fn st_visibility(&self) -> u8 { self.st_other & 0x3 } } /// Additional information about a `Sym32`. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Syminfo32 { /// Direct bindings, symbol bound to. pub si_boundto: U16, /// Per symbol flags. pub si_flags: U16, } /// Additional information about a `Sym64`. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Syminfo64 { /// Direct bindings, symbol bound to. pub si_boundto: U16, /// Per symbol flags. pub si_flags: U16, } // Values for `Syminfo*::si_boundto`. /// Symbol bound to self pub const SYMINFO_BT_SELF: u16 = 0xffff; /// Symbol bound to parent pub const SYMINFO_BT_PARENT: u16 = 0xfffe; /// Beginning of reserved entries pub const SYMINFO_BT_LOWRESERVE: u16 = 0xff00; // Values for `Syminfo*::si_flags`. /// Direct bound symbol pub const SYMINFO_FLG_DIRECT: u16 = 0x0001; /// Pass-thru symbol for translator pub const SYMINFO_FLG_PASSTHRU: u16 = 0x0002; /// Symbol is a copy-reloc pub const SYMINFO_FLG_COPY: u16 = 0x0004; /// Symbol bound to object to be lazy loaded pub const SYMINFO_FLG_LAZYLOAD: u16 = 0x0008; // Syminfo version values. pub const SYMINFO_NONE: u16 = 0; pub const SYMINFO_CURRENT: u16 = 1; pub const SYMINFO_NUM: u16 = 2; // Values for bind component of `Sym*::st_info`. /// Local symbol. pub const STB_LOCAL: u8 = 0; /// Global symbol. pub const STB_GLOBAL: u8 = 1; /// Weak symbol. pub const STB_WEAK: u8 = 2; /// Start of OS-specific symbol binding. pub const STB_LOOS: u8 = 10; /// Unique symbol. pub const STB_GNU_UNIQUE: u8 = 10; /// End of OS-specific symbol binding. pub const STB_HIOS: u8 = 12; /// Start of processor-specific symbol binding. pub const STB_LOPROC: u8 = 13; /// End of processor-specific symbol binding. pub const STB_HIPROC: u8 = 15; // Values for type component of `Sym*::st_info`. /// Symbol type is unspecified. pub const STT_NOTYPE: u8 = 0; /// Symbol is a data object. pub const STT_OBJECT: u8 = 1; /// Symbol is a code object. pub const STT_FUNC: u8 = 2; /// Symbol is associated with a section. pub const STT_SECTION: u8 = 3; /// Symbol's name is a file name. pub const STT_FILE: u8 = 4; /// Symbol is a common data object. pub const STT_COMMON: u8 = 5; /// Symbol is a thread-local storage object. pub const STT_TLS: u8 = 6; /// Start of OS-specific symbol types. pub const STT_LOOS: u8 = 10; /// Symbol is an indirect code object. pub const STT_GNU_IFUNC: u8 = 10; /// End of OS-specific symbol types. pub const STT_HIOS: u8 = 12; /// Start of processor-specific symbol types. pub const STT_LOPROC: u8 = 13; /// End of processor-specific symbol types. pub const STT_HIPROC: u8 = 15; // Values for visibility component of `Symbol*::st_other`. /// Default symbol visibility rules. pub const STV_DEFAULT: u8 = 0; /// Processor specific hidden class. pub const STV_INTERNAL: u8 = 1; /// Symbol is not visible to other components. pub const STV_HIDDEN: u8 = 2; /// Symbol is visible to other components, but is not preemptible. pub const STV_PROTECTED: u8 = 3; /// Relocation table entry without explicit addend. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Rel32 { /// Relocation address. pub r_offset: U32, /// Relocation type and symbol index. pub r_info: U32, } impl Rel32 { /// Get the `r_sym` component of the `r_info` field. #[inline] pub fn r_sym(&self, endian: E) -> u32 { self.r_info.get(endian) >> 8 } /// Get the `r_type` component of the `r_info` field. #[inline] pub fn r_type(&self, endian: E) -> u32 { self.r_info.get(endian) & 0xff } /// Calculate the `r_info` field given the `r_sym` and `r_type` components. pub fn r_info(endian: E, r_sym: u32, r_type: u8) -> U32 { U32::new(endian, (r_sym << 8) | u32::from(r_type)) } /// Set the `r_info` field given the `r_sym` and `r_type` components. pub fn set_r_info(&mut self, endian: E, r_sym: u32, r_type: u8) { self.r_info = Self::r_info(endian, r_sym, r_type) } } /// Relocation table entry with explicit addend. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Rela32 { /// Relocation address. pub r_offset: U32, /// Relocation type and symbol index. pub r_info: U32, /// Explicit addend. pub r_addend: I32, } impl Rela32 { /// Get the `r_sym` component of the `r_info` field. #[inline] pub fn r_sym(&self, endian: E) -> u32 { self.r_info.get(endian) >> 8 } /// Get the `r_type` component of the `r_info` field. #[inline] pub fn r_type(&self, endian: E) -> u32 { self.r_info.get(endian) & 0xff } /// Calculate the `r_info` field given the `r_sym` and `r_type` components. pub fn r_info(endian: E, r_sym: u32, r_type: u8) -> U32 { U32::new(endian, (r_sym << 8) | u32::from(r_type)) } /// Set the `r_info` field given the `r_sym` and `r_type` components. pub fn set_r_info(&mut self, endian: E, r_sym: u32, r_type: u8) { self.r_info = Self::r_info(endian, r_sym, r_type) } } impl From> for Rela32 { fn from(rel: Rel32) -> Self { Rela32 { r_offset: rel.r_offset, r_info: rel.r_info, r_addend: I32::default(), } } } /// Relocation table entry without explicit addend. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Rel64 { /// Relocation address. pub r_offset: U64, /// Relocation type and symbol index. pub r_info: U64, } impl Rel64 { /// Get the `r_sym` component of the `r_info` field. #[inline] pub fn r_sym(&self, endian: E) -> u32 { (self.r_info.get(endian) >> 32) as u32 } /// Get the `r_type` component of the `r_info` field. #[inline] pub fn r_type(&self, endian: E) -> u32 { (self.r_info.get(endian) & 0xffff_ffff) as u32 } /// Calculate the `r_info` field given the `r_sym` and `r_type` components. pub fn r_info(endian: E, r_sym: u32, r_type: u32) -> U64 { U64::new(endian, (u64::from(r_sym) << 32) | u64::from(r_type)) } /// Set the `r_info` field given the `r_sym` and `r_type` components. pub fn set_r_info(&mut self, endian: E, r_sym: u32, r_type: u32) { self.r_info = Self::r_info(endian, r_sym, r_type) } } impl From> for Rela64 { fn from(rel: Rel64) -> Self { Rela64 { r_offset: rel.r_offset, r_info: rel.r_info, r_addend: I64::default(), } } } /// Relocation table entry with explicit addend. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Rela64 { /// Relocation address. pub r_offset: U64, /// Relocation type and symbol index. pub r_info: U64, /// Explicit addend. pub r_addend: I64, } impl Rela64 { pub(crate) fn get_r_info(&self, endian: E, is_mips64el: bool) -> u64 { let mut t = self.r_info.get(endian); if is_mips64el { t = (t << 32) | ((t >> 8) & 0xff000000) | ((t >> 24) & 0x00ff0000) | ((t >> 40) & 0x0000ff00) | ((t >> 56) & 0x000000ff); } t } /// Get the `r_sym` component of the `r_info` field. #[inline] pub fn r_sym(&self, endian: E, is_mips64el: bool) -> u32 { (self.get_r_info(endian, is_mips64el) >> 32) as u32 } /// Get the `r_type` component of the `r_info` field. #[inline] pub fn r_type(&self, endian: E, is_mips64el: bool) -> u32 { (self.get_r_info(endian, is_mips64el) & 0xffff_ffff) as u32 } /// Calculate the `r_info` field given the `r_sym` and `r_type` components. pub fn r_info(endian: E, is_mips64el: bool, r_sym: u32, r_type: u32) -> U64 { let mut t = (u64::from(r_sym) << 32) | u64::from(r_type); if is_mips64el { t = (t >> 32) | ((t & 0xff000000) << 8) | ((t & 0x00ff0000) << 24) | ((t & 0x0000ff00) << 40) | ((t & 0x000000ff) << 56); } U64::new(endian, t) } /// Set the `r_info` field given the `r_sym` and `r_type` components. pub fn set_r_info(&mut self, endian: E, is_mips64el: bool, r_sym: u32, r_type: u32) { self.r_info = Self::r_info(endian, is_mips64el, r_sym, r_type); } } /// Program segment header. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ProgramHeader32 { /// Segment type. One of the `PT_*` constants. pub p_type: U32, /// Segment file offset. pub p_offset: U32, /// Segment virtual address. pub p_vaddr: U32, /// Segment physical address. pub p_paddr: U32, /// Segment size in the file. pub p_filesz: U32, /// Segment size in memory. pub p_memsz: U32, /// Segment flags. A combination of the `PF_*` constants. pub p_flags: U32, /// Segment alignment. pub p_align: U32, } /// Program segment header. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ProgramHeader64 { /// Segment type. One of the `PT_*` constants. pub p_type: U32, /// Segment flags. A combination of the `PF_*` constants. pub p_flags: U32, /// Segment file offset. pub p_offset: U64, /// Segment virtual address. pub p_vaddr: U64, /// Segment physical address. pub p_paddr: U64, /// Segment size in the file. pub p_filesz: U64, /// Segment size in memory. pub p_memsz: U64, /// Segment alignment. pub p_align: U64, } /// Special value for `FileHeader*::e_phnum`. /// /// This indicates that the real number of program headers is too large to fit into e_phnum. /// Instead the real value is in the field `sh_info` of section 0. pub const PN_XNUM: u16 = 0xffff; // Values for `ProgramHeader*::p_type`. /// Program header table entry is unused. pub const PT_NULL: u32 = 0; /// Loadable program segment. pub const PT_LOAD: u32 = 1; /// Dynamic linking information. pub const PT_DYNAMIC: u32 = 2; /// Program interpreter. pub const PT_INTERP: u32 = 3; /// Auxiliary information. pub const PT_NOTE: u32 = 4; /// Reserved. pub const PT_SHLIB: u32 = 5; /// Segment contains the program header table. pub const PT_PHDR: u32 = 6; /// Thread-local storage segment. pub const PT_TLS: u32 = 7; /// Start of OS-specific segment types. pub const PT_LOOS: u32 = 0x6000_0000; /// GCC `.eh_frame_hdr` segment. pub const PT_GNU_EH_FRAME: u32 = 0x6474_e550; /// Indicates stack executability. pub const PT_GNU_STACK: u32 = 0x6474_e551; /// Read-only after relocation. pub const PT_GNU_RELRO: u32 = 0x6474_e552; /// Segment containing `.note.gnu.property` section. pub const PT_GNU_PROPERTY: u32 = 0x6474_e553; /// End of OS-specific segment types. pub const PT_HIOS: u32 = 0x6fff_ffff; /// Start of processor-specific segment types. pub const PT_LOPROC: u32 = 0x7000_0000; /// End of processor-specific segment types. pub const PT_HIPROC: u32 = 0x7fff_ffff; // Values for `ProgramHeader*::p_flags`. /// Segment is executable. pub const PF_X: u32 = 1 << 0; /// Segment is writable. pub const PF_W: u32 = 1 << 1; /// Segment is readable. pub const PF_R: u32 = 1 << 2; /// OS-specific segment flags. pub const PF_MASKOS: u32 = 0x0ff0_0000; /// Processor-specific segment flags. pub const PF_MASKPROC: u32 = 0xf000_0000; /// Note name for core files. pub const ELF_NOTE_CORE: &[u8] = b"CORE"; /// Note name for linux core files. /// /// Notes in linux core files may also use `ELF_NOTE_CORE`. pub const ELF_NOTE_LINUX: &[u8] = b"LINUX"; // Values for `NoteHeader*::n_type` in core files. // /// Contains copy of prstatus struct. pub const NT_PRSTATUS: u32 = 1; /// Contains copy of fpregset struct. pub const NT_PRFPREG: u32 = 2; /// Contains copy of fpregset struct. pub const NT_FPREGSET: u32 = 2; /// Contains copy of prpsinfo struct. pub const NT_PRPSINFO: u32 = 3; /// Contains copy of prxregset struct. pub const NT_PRXREG: u32 = 4; /// Contains copy of task structure. pub const NT_TASKSTRUCT: u32 = 4; /// String from sysinfo(SI_PLATFORM). pub const NT_PLATFORM: u32 = 5; /// Contains copy of auxv array. pub const NT_AUXV: u32 = 6; /// Contains copy of gwindows struct. pub const NT_GWINDOWS: u32 = 7; /// Contains copy of asrset struct. pub const NT_ASRS: u32 = 8; /// Contains copy of pstatus struct. pub const NT_PSTATUS: u32 = 10; /// Contains copy of psinfo struct. pub const NT_PSINFO: u32 = 13; /// Contains copy of prcred struct. pub const NT_PRCRED: u32 = 14; /// Contains copy of utsname struct. pub const NT_UTSNAME: u32 = 15; /// Contains copy of lwpstatus struct. pub const NT_LWPSTATUS: u32 = 16; /// Contains copy of lwpinfo struct. pub const NT_LWPSINFO: u32 = 17; /// Contains copy of fprxregset struct. pub const NT_PRFPXREG: u32 = 20; /// Contains copy of siginfo_t, size might increase. pub const NT_SIGINFO: u32 = 0x5349_4749; /// Contains information about mapped files. pub const NT_FILE: u32 = 0x4649_4c45; /// Contains copy of user_fxsr_struct. pub const NT_PRXFPREG: u32 = 0x46e6_2b7f; /// PowerPC Altivec/VMX registers. pub const NT_PPC_VMX: u32 = 0x100; /// PowerPC SPE/EVR registers. pub const NT_PPC_SPE: u32 = 0x101; /// PowerPC VSX registers. pub const NT_PPC_VSX: u32 = 0x102; /// Target Address Register. pub const NT_PPC_TAR: u32 = 0x103; /// Program Priority Register. pub const NT_PPC_PPR: u32 = 0x104; /// Data Stream Control Register. pub const NT_PPC_DSCR: u32 = 0x105; /// Event Based Branch Registers. pub const NT_PPC_EBB: u32 = 0x106; /// Performance Monitor Registers. pub const NT_PPC_PMU: u32 = 0x107; /// TM checkpointed GPR Registers. pub const NT_PPC_TM_CGPR: u32 = 0x108; /// TM checkpointed FPR Registers. pub const NT_PPC_TM_CFPR: u32 = 0x109; /// TM checkpointed VMX Registers. pub const NT_PPC_TM_CVMX: u32 = 0x10a; /// TM checkpointed VSX Registers. pub const NT_PPC_TM_CVSX: u32 = 0x10b; /// TM Special Purpose Registers. pub const NT_PPC_TM_SPR: u32 = 0x10c; /// TM checkpointed Target Address Register. pub const NT_PPC_TM_CTAR: u32 = 0x10d; /// TM checkpointed Program Priority Register. pub const NT_PPC_TM_CPPR: u32 = 0x10e; /// TM checkpointed Data Stream Control Register. pub const NT_PPC_TM_CDSCR: u32 = 0x10f; /// Memory Protection Keys registers. pub const NT_PPC_PKEY: u32 = 0x110; /// i386 TLS slots (struct user_desc). pub const NT_386_TLS: u32 = 0x200; /// x86 io permission bitmap (1=deny). pub const NT_386_IOPERM: u32 = 0x201; /// x86 extended state using xsave. pub const NT_X86_XSTATE: u32 = 0x202; /// s390 upper register halves. pub const NT_S390_HIGH_GPRS: u32 = 0x300; /// s390 timer register. pub const NT_S390_TIMER: u32 = 0x301; /// s390 TOD clock comparator register. pub const NT_S390_TODCMP: u32 = 0x302; /// s390 TOD programmable register. pub const NT_S390_TODPREG: u32 = 0x303; /// s390 control registers. pub const NT_S390_CTRS: u32 = 0x304; /// s390 prefix register. pub const NT_S390_PREFIX: u32 = 0x305; /// s390 breaking event address. pub const NT_S390_LAST_BREAK: u32 = 0x306; /// s390 system call restart data. pub const NT_S390_SYSTEM_CALL: u32 = 0x307; /// s390 transaction diagnostic block. pub const NT_S390_TDB: u32 = 0x308; /// s390 vector registers 0-15 upper half. pub const NT_S390_VXRS_LOW: u32 = 0x309; /// s390 vector registers 16-31. pub const NT_S390_VXRS_HIGH: u32 = 0x30a; /// s390 guarded storage registers. pub const NT_S390_GS_CB: u32 = 0x30b; /// s390 guarded storage broadcast control block. pub const NT_S390_GS_BC: u32 = 0x30c; /// s390 runtime instrumentation. pub const NT_S390_RI_CB: u32 = 0x30d; /// ARM VFP/NEON registers. pub const NT_ARM_VFP: u32 = 0x400; /// ARM TLS register. pub const NT_ARM_TLS: u32 = 0x401; /// ARM hardware breakpoint registers. pub const NT_ARM_HW_BREAK: u32 = 0x402; /// ARM hardware watchpoint registers. pub const NT_ARM_HW_WATCH: u32 = 0x403; /// ARM system call number. pub const NT_ARM_SYSTEM_CALL: u32 = 0x404; /// ARM Scalable Vector Extension registers. pub const NT_ARM_SVE: u32 = 0x405; /// Vmcore Device Dump Note. pub const NT_VMCOREDD: u32 = 0x700; /// MIPS DSP ASE registers. pub const NT_MIPS_DSP: u32 = 0x800; /// MIPS floating-point mode. pub const NT_MIPS_FP_MODE: u32 = 0x801; /// Note type for version string. /// /// This note may appear in object files. /// /// It must be handled as a special case because it has no descriptor, and instead /// uses the note name as the version string. pub const NT_VERSION: u32 = 1; /// Dynamic section entry. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Dyn32 { /// Dynamic entry type. pub d_tag: U32, /// Value (integer or address). pub d_val: U32, } /// Dynamic section entry. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Dyn64 { /// Dynamic entry type. pub d_tag: U64, /// Value (integer or address). pub d_val: U64, } // Values for `Dyn*::d_tag`. /// Marks end of dynamic section pub const DT_NULL: u32 = 0; /// Name of needed library pub const DT_NEEDED: u32 = 1; /// Size in bytes of PLT relocs pub const DT_PLTRELSZ: u32 = 2; /// Processor defined value pub const DT_PLTGOT: u32 = 3; /// Address of symbol hash table pub const DT_HASH: u32 = 4; /// Address of string table pub const DT_STRTAB: u32 = 5; /// Address of symbol table pub const DT_SYMTAB: u32 = 6; /// Address of Rela relocs pub const DT_RELA: u32 = 7; /// Total size of Rela relocs pub const DT_RELASZ: u32 = 8; /// Size of one Rela reloc pub const DT_RELAENT: u32 = 9; /// Size of string table pub const DT_STRSZ: u32 = 10; /// Size of one symbol table entry pub const DT_SYMENT: u32 = 11; /// Address of init function pub const DT_INIT: u32 = 12; /// Address of termination function pub const DT_FINI: u32 = 13; /// Name of shared object pub const DT_SONAME: u32 = 14; /// Library search path (deprecated) pub const DT_RPATH: u32 = 15; /// Start symbol search here pub const DT_SYMBOLIC: u32 = 16; /// Address of Rel relocs pub const DT_REL: u32 = 17; /// Total size of Rel relocs pub const DT_RELSZ: u32 = 18; /// Size of one Rel reloc pub const DT_RELENT: u32 = 19; /// Type of reloc in PLT pub const DT_PLTREL: u32 = 20; /// For debugging; unspecified pub const DT_DEBUG: u32 = 21; /// Reloc might modify .text pub const DT_TEXTREL: u32 = 22; /// Address of PLT relocs pub const DT_JMPREL: u32 = 23; /// Process relocations of object pub const DT_BIND_NOW: u32 = 24; /// Array with addresses of init fct pub const DT_INIT_ARRAY: u32 = 25; /// Array with addresses of fini fct pub const DT_FINI_ARRAY: u32 = 26; /// Size in bytes of DT_INIT_ARRAY pub const DT_INIT_ARRAYSZ: u32 = 27; /// Size in bytes of DT_FINI_ARRAY pub const DT_FINI_ARRAYSZ: u32 = 28; /// Library search path pub const DT_RUNPATH: u32 = 29; /// Flags for the object being loaded pub const DT_FLAGS: u32 = 30; /// Start of encoded range pub const DT_ENCODING: u32 = 32; /// Array with addresses of preinit fct pub const DT_PREINIT_ARRAY: u32 = 32; /// size in bytes of DT_PREINIT_ARRAY pub const DT_PREINIT_ARRAYSZ: u32 = 33; /// Address of SYMTAB_SHNDX section pub const DT_SYMTAB_SHNDX: u32 = 34; /// Start of OS-specific pub const DT_LOOS: u32 = 0x6000_000d; /// End of OS-specific pub const DT_HIOS: u32 = 0x6fff_f000; /// Start of processor-specific pub const DT_LOPROC: u32 = 0x7000_0000; /// End of processor-specific pub const DT_HIPROC: u32 = 0x7fff_ffff; // `DT_*` entries between `DT_VALRNGHI` & `DT_VALRNGLO` use `d_val` as a value. pub const DT_VALRNGLO: u32 = 0x6fff_fd00; /// Prelinking timestamp pub const DT_GNU_PRELINKED: u32 = 0x6fff_fdf5; /// Size of conflict section pub const DT_GNU_CONFLICTSZ: u32 = 0x6fff_fdf6; /// Size of library list pub const DT_GNU_LIBLISTSZ: u32 = 0x6fff_fdf7; pub const DT_CHECKSUM: u32 = 0x6fff_fdf8; pub const DT_PLTPADSZ: u32 = 0x6fff_fdf9; pub const DT_MOVEENT: u32 = 0x6fff_fdfa; pub const DT_MOVESZ: u32 = 0x6fff_fdfb; /// Feature selection (DTF_*). pub const DT_FEATURE_1: u32 = 0x6fff_fdfc; /// Flags for DT_* entries, affecting the following DT_* entry. pub const DT_POSFLAG_1: u32 = 0x6fff_fdfd; /// Size of syminfo table (in bytes) pub const DT_SYMINSZ: u32 = 0x6fff_fdfe; /// Entry size of syminfo pub const DT_SYMINENT: u32 = 0x6fff_fdff; pub const DT_VALRNGHI: u32 = 0x6fff_fdff; // `DT_*` entries between `DT_ADDRRNGHI` & `DT_ADDRRNGLO` use `d_val` as an address. // // If any adjustment is made to the ELF object after it has been // built these entries will need to be adjusted. pub const DT_ADDRRNGLO: u32 = 0x6fff_fe00; /// GNU-style hash table. pub const DT_GNU_HASH: u32 = 0x6fff_fef5; pub const DT_TLSDESC_PLT: u32 = 0x6fff_fef6; pub const DT_TLSDESC_GOT: u32 = 0x6fff_fef7; /// Start of conflict section pub const DT_GNU_CONFLICT: u32 = 0x6fff_fef8; /// Library list pub const DT_GNU_LIBLIST: u32 = 0x6fff_fef9; /// Configuration information. pub const DT_CONFIG: u32 = 0x6fff_fefa; /// Dependency auditing. pub const DT_DEPAUDIT: u32 = 0x6fff_fefb; /// Object auditing. pub const DT_AUDIT: u32 = 0x6fff_fefc; /// PLT padding. pub const DT_PLTPAD: u32 = 0x6fff_fefd; /// Move table. pub const DT_MOVETAB: u32 = 0x6fff_fefe; /// Syminfo table. pub const DT_SYMINFO: u32 = 0x6fff_feff; pub const DT_ADDRRNGHI: u32 = 0x6fff_feff; // The versioning entry types. The next are defined as part of the // GNU extension. pub const DT_VERSYM: u32 = 0x6fff_fff0; pub const DT_RELACOUNT: u32 = 0x6fff_fff9; pub const DT_RELCOUNT: u32 = 0x6fff_fffa; /// State flags, see DF_1_* below. pub const DT_FLAGS_1: u32 = 0x6fff_fffb; /// Address of version definition table pub const DT_VERDEF: u32 = 0x6fff_fffc; /// Number of version definitions pub const DT_VERDEFNUM: u32 = 0x6fff_fffd; /// Address of table with needed versions pub const DT_VERNEED: u32 = 0x6fff_fffe; /// Number of needed versions pub const DT_VERNEEDNUM: u32 = 0x6fff_ffff; // Machine-independent extensions in the "processor-specific" range. /// Shared object to load before self pub const DT_AUXILIARY: u32 = 0x7fff_fffd; /// Shared object to get values from pub const DT_FILTER: u32 = 0x7fff_ffff; // Values of `Dyn*::d_val` in the `DT_FLAGS` entry. /// Object may use DF_ORIGIN pub const DF_ORIGIN: u32 = 0x0000_0001; /// Symbol resolutions starts here pub const DF_SYMBOLIC: u32 = 0x0000_0002; /// Object contains text relocations pub const DF_TEXTREL: u32 = 0x0000_0004; /// No lazy binding for this object pub const DF_BIND_NOW: u32 = 0x0000_0008; /// Module uses the static TLS model pub const DF_STATIC_TLS: u32 = 0x0000_0010; // Values of `Dyn*::d_val` in the `DT_FLAGS_1` entry. /// Set RTLD_NOW for this object. pub const DF_1_NOW: u32 = 0x0000_0001; /// Set RTLD_GLOBAL for this object. pub const DF_1_GLOBAL: u32 = 0x0000_0002; /// Set RTLD_GROUP for this object. pub const DF_1_GROUP: u32 = 0x0000_0004; /// Set RTLD_NODELETE for this object. pub const DF_1_NODELETE: u32 = 0x0000_0008; /// Trigger filtee loading at runtime. pub const DF_1_LOADFLTR: u32 = 0x0000_0010; /// Set RTLD_INITFIRST for this object. pub const DF_1_INITFIRST: u32 = 0x0000_0020; /// Set RTLD_NOOPEN for this object. pub const DF_1_NOOPEN: u32 = 0x0000_0040; /// $ORIGIN must be handled. pub const DF_1_ORIGIN: u32 = 0x0000_0080; /// Direct binding enabled. pub const DF_1_DIRECT: u32 = 0x0000_0100; pub const DF_1_TRANS: u32 = 0x0000_0200; /// Object is used to interpose. pub const DF_1_INTERPOSE: u32 = 0x0000_0400; /// Ignore default lib search path. pub const DF_1_NODEFLIB: u32 = 0x0000_0800; /// Object can't be dldump'ed. pub const DF_1_NODUMP: u32 = 0x0000_1000; /// Configuration alternative created. pub const DF_1_CONFALT: u32 = 0x0000_2000; /// Filtee terminates filters search. pub const DF_1_ENDFILTEE: u32 = 0x0000_4000; /// Disp reloc applied at build time. pub const DF_1_DISPRELDNE: u32 = 0x0000_8000; /// Disp reloc applied at run-time. pub const DF_1_DISPRELPND: u32 = 0x0001_0000; /// Object has no-direct binding. pub const DF_1_NODIRECT: u32 = 0x0002_0000; pub const DF_1_IGNMULDEF: u32 = 0x0004_0000; pub const DF_1_NOKSYMS: u32 = 0x0008_0000; pub const DF_1_NOHDR: u32 = 0x0010_0000; /// Object is modified after built. pub const DF_1_EDITED: u32 = 0x0020_0000; pub const DF_1_NORELOC: u32 = 0x0040_0000; /// Object has individual interposers. pub const DF_1_SYMINTPOSE: u32 = 0x0080_0000; /// Global auditing required. pub const DF_1_GLOBAUDIT: u32 = 0x0100_0000; /// Singleton symbols are used. pub const DF_1_SINGLETON: u32 = 0x0200_0000; pub const DF_1_STUB: u32 = 0x0400_0000; pub const DF_1_PIE: u32 = 0x0800_0000; /// Version symbol information #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Versym(pub U16); /// Symbol is hidden. pub const VERSYM_HIDDEN: u16 = 0x8000; /// Symbol version index. pub const VERSYM_VERSION: u16 = 0x7fff; /// Version definition sections #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Verdef { /// Version revision pub vd_version: U16, /// Version information pub vd_flags: U16, /// Version Index pub vd_ndx: U16, /// Number of associated aux entries pub vd_cnt: U16, /// Version name hash value pub vd_hash: U32, /// Offset in bytes to verdaux array pub vd_aux: U32, /// Offset in bytes to next verdef entry pub vd_next: U32, } // Legal values for vd_version (version revision). /// No version pub const VER_DEF_NONE: u16 = 0; /// Current version pub const VER_DEF_CURRENT: u16 = 1; // Legal values for vd_flags (version information flags). /// Version definition of file itself pub const VER_FLG_BASE: u16 = 0x1; // Legal values for vd_flags and vna_flags (version information flags). /// Weak version identifier pub const VER_FLG_WEAK: u16 = 0x2; // Versym symbol index values. /// Symbol is local. pub const VER_NDX_LOCAL: u16 = 0; /// Symbol is global. pub const VER_NDX_GLOBAL: u16 = 1; /// Auxiliary version information. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Verdaux { /// Version or dependency names pub vda_name: U32, /// Offset in bytes to next verdaux pub vda_next: U32, } /// Version dependency. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Verneed { /// Version of structure pub vn_version: U16, /// Number of associated aux entries pub vn_cnt: U16, /// Offset of filename for this dependency pub vn_file: U32, /// Offset in bytes to vernaux array pub vn_aux: U32, /// Offset in bytes to next verneed entry pub vn_next: U32, } // Legal values for vn_version (version revision). /// No version pub const VER_NEED_NONE: u16 = 0; /// Current version pub const VER_NEED_CURRENT: u16 = 1; /// Auxiliary needed version information. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Vernaux { /// Hash value of dependency name pub vna_hash: U32, /// Dependency specific information pub vna_flags: U16, /// Version Index pub vna_other: U16, /// Dependency name string offset pub vna_name: U32, /// Offset in bytes to next vernaux entry pub vna_next: U32, } // TODO: Elf*_auxv_t, AT_* /// Note section entry header. /// /// A note consists of a header followed by a variable length name and descriptor. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct NoteHeader32 { /// Length of the note's name. /// /// Some known names are defined by the `ELF_NOTE_*` constants. pub n_namesz: U32, /// Length of the note's descriptor. /// /// The content of the descriptor depends on the note name and type. pub n_descsz: U32, /// Type of the note. /// /// One of the `NT_*` constants. The note name determines which /// `NT_*` constants are valid. pub n_type: U32, } /// Note section entry header. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct NoteHeader64 { /// Length of the note's name. /// /// Some known names are defined by the `ELF_NOTE_*` constants. pub n_namesz: U32, /// Length of the note's descriptor. /// /// The content of the descriptor depends on the note name and type. pub n_descsz: U32, /// Type of the note. /// /// One of the `NT_*` constants. The note name determines which /// `NT_*` constants are valid. pub n_type: U32, } /// Solaris entries in the note section have this name. pub const ELF_NOTE_SOLARIS: &[u8] = b"SUNW Solaris"; // Values for `n_type` when the name is `ELF_NOTE_SOLARIS`. /// Desired pagesize for the binary. pub const NT_SOLARIS_PAGESIZE_HINT: u32 = 1; /// GNU entries in the note section have this name. pub const ELF_NOTE_GNU: &[u8] = b"GNU"; /// Go entries in the note section have this name. // See https://go-review.googlesource.com/9520 and https://go-review.googlesource.com/10704. pub const ELF_NOTE_GO: &[u8] = b"Go"; // Note types for `ELF_NOTE_GNU`. /// ABI information. /// /// The descriptor consists of words: /// - word 0: OS descriptor /// - word 1: major version of the ABI /// - word 2: minor version of the ABI /// - word 3: subminor version of the ABI pub const NT_GNU_ABI_TAG: u32 = 1; /// OS descriptor for `NT_GNU_ABI_TAG`. pub const ELF_NOTE_OS_LINUX: u32 = 0; /// OS descriptor for `NT_GNU_ABI_TAG`. pub const ELF_NOTE_OS_GNU: u32 = 1; /// OS descriptor for `NT_GNU_ABI_TAG`. pub const ELF_NOTE_OS_SOLARIS2: u32 = 2; /// OS descriptor for `NT_GNU_ABI_TAG`. pub const ELF_NOTE_OS_FREEBSD: u32 = 3; /// Synthetic hwcap information. /// /// The descriptor begins with two words: /// - word 0: number of entries /// - word 1: bitmask of enabled entries /// /// Then follow variable-length entries, one byte followed by a /// '\0'-terminated hwcap name string. The byte gives the bit /// number to test if enabled, (1U << bit) & bitmask. */ pub const NT_GNU_HWCAP: u32 = 2; /// Build ID bits as generated by `ld --build-id`. /// /// The descriptor consists of any nonzero number of bytes. pub const NT_GNU_BUILD_ID: u32 = 3; /// Build ID bits as generated by Go's gc compiler. /// /// The descriptor consists of any nonzero number of bytes. // See https://go-review.googlesource.com/10707. pub const NT_GO_BUILD_ID: u32 = 4; /// Version note generated by GNU gold containing a version string. pub const NT_GNU_GOLD_VERSION: u32 = 4; /// Program property. pub const NT_GNU_PROPERTY_TYPE_0: u32 = 5; // Values used in GNU .note.gnu.property notes (NT_GNU_PROPERTY_TYPE_0). /// Stack size. pub const GNU_PROPERTY_STACK_SIZE: u32 = 1; /// No copy relocation on protected data symbol. pub const GNU_PROPERTY_NO_COPY_ON_PROTECTED: u32 = 2; // A 4-byte unsigned integer property: A bit is set if it is set in all // relocatable inputs. pub const GNU_PROPERTY_UINT32_AND_LO: u32 = 0xb0000000; pub const GNU_PROPERTY_UINT32_AND_HI: u32 = 0xb0007fff; // A 4-byte unsigned integer property: A bit is set if it is set in any // relocatable inputs. pub const GNU_PROPERTY_UINT32_OR_LO: u32 = 0xb0008000; pub const GNU_PROPERTY_UINT32_OR_HI: u32 = 0xb000ffff; /// The needed properties by the object file. */ pub const GNU_PROPERTY_1_NEEDED: u32 = GNU_PROPERTY_UINT32_OR_LO; /// Set if the object file requires canonical function pointers and /// cannot be used with copy relocation. pub const GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS: u32 = 1 << 0; /// Processor-specific semantics, lo pub const GNU_PROPERTY_LOPROC: u32 = 0xc0000000; /// Processor-specific semantics, hi pub const GNU_PROPERTY_HIPROC: u32 = 0xdfffffff; /// Application-specific semantics, lo pub const GNU_PROPERTY_LOUSER: u32 = 0xe0000000; /// Application-specific semantics, hi pub const GNU_PROPERTY_HIUSER: u32 = 0xffffffff; /// AArch64 specific GNU properties. pub const GNU_PROPERTY_AARCH64_FEATURE_1_AND: u32 = 0xc0000000; pub const GNU_PROPERTY_AARCH64_FEATURE_PAUTH: u32 = 0xc0000001; pub const GNU_PROPERTY_AARCH64_FEATURE_1_BTI: u32 = 1 << 0; pub const GNU_PROPERTY_AARCH64_FEATURE_1_PAC: u32 = 1 << 1; // A 4-byte unsigned integer property: A bit is set if it is set in all // relocatable inputs. pub const GNU_PROPERTY_X86_UINT32_AND_LO: u32 = 0xc0000002; pub const GNU_PROPERTY_X86_UINT32_AND_HI: u32 = 0xc0007fff; // A 4-byte unsigned integer property: A bit is set if it is set in any // relocatable inputs. pub const GNU_PROPERTY_X86_UINT32_OR_LO: u32 = 0xc0008000; pub const GNU_PROPERTY_X86_UINT32_OR_HI: u32 = 0xc000ffff; // A 4-byte unsigned integer property: A bit is set if it is set in any // relocatable inputs and the property is present in all relocatable // inputs. pub const GNU_PROPERTY_X86_UINT32_OR_AND_LO: u32 = 0xc0010000; pub const GNU_PROPERTY_X86_UINT32_OR_AND_HI: u32 = 0xc0017fff; /// The x86 instruction sets indicated by the corresponding bits are /// used in program. Their support in the hardware is optional. pub const GNU_PROPERTY_X86_ISA_1_USED: u32 = 0xc0010002; /// The x86 instruction sets indicated by the corresponding bits are /// used in program and they must be supported by the hardware. pub const GNU_PROPERTY_X86_ISA_1_NEEDED: u32 = 0xc0008002; /// X86 processor-specific features used in program. pub const GNU_PROPERTY_X86_FEATURE_1_AND: u32 = 0xc0000002; /// GNU_PROPERTY_X86_ISA_1_BASELINE: CMOV, CX8 (cmpxchg8b), FPU (fld), /// MMX, OSFXSR (fxsave), SCE (syscall), SSE and SSE2. pub const GNU_PROPERTY_X86_ISA_1_BASELINE: u32 = 1 << 0; /// GNU_PROPERTY_X86_ISA_1_V2: GNU_PROPERTY_X86_ISA_1_BASELINE, /// CMPXCHG16B (cmpxchg16b), LAHF-SAHF (lahf), POPCNT (popcnt), SSE3, /// SSSE3, SSE4.1 and SSE4.2. pub const GNU_PROPERTY_X86_ISA_1_V2: u32 = 1 << 1; /// GNU_PROPERTY_X86_ISA_1_V3: GNU_PROPERTY_X86_ISA_1_V2, AVX, AVX2, BMI1, /// BMI2, F16C, FMA, LZCNT, MOVBE, XSAVE. pub const GNU_PROPERTY_X86_ISA_1_V3: u32 = 1 << 2; /// GNU_PROPERTY_X86_ISA_1_V4: GNU_PROPERTY_X86_ISA_1_V3, AVX512F, /// AVX512BW, AVX512CD, AVX512DQ and AVX512VL. pub const GNU_PROPERTY_X86_ISA_1_V4: u32 = 1 << 3; /// This indicates that all executable sections are compatible with IBT. pub const GNU_PROPERTY_X86_FEATURE_1_IBT: u32 = 1 << 0; /// This indicates that all executable sections are compatible with SHSTK. pub const GNU_PROPERTY_X86_FEATURE_1_SHSTK: u32 = 1 << 1; // TODO: Elf*_Move /// Header of `SHT_HASH` section. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct HashHeader { /// The number of hash buckets. pub bucket_count: U32, /// The number of chain values. pub chain_count: U32, // Array of hash bucket start indices. // buckets: U32[bucket_count] // Array of hash chain links. An index of 0 terminates the chain. // chains: U32[chain_count] } /// Calculate the SysV hash for a symbol name. /// /// Used for `SHT_HASH`. pub fn hash(name: &[u8]) -> u32 { let mut hash = 0u32; for byte in name { hash = hash.wrapping_mul(16).wrapping_add(u32::from(*byte)); hash ^= (hash >> 24) & 0xf0; } hash & 0xfff_ffff } /// Header of `SHT_GNU_HASH` section. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct GnuHashHeader { /// The number of hash buckets. pub bucket_count: U32, /// The symbol table index of the first symbol in the hash. pub symbol_base: U32, /// The number of words in the bloom filter. /// /// Must be a non-zero power of 2. pub bloom_count: U32, /// The bit shift count for the bloom filter. pub bloom_shift: U32, // Array of bloom filter words. // bloom_filters: U32[bloom_count] or U64[bloom_count] // Array of hash bucket start indices. // buckets: U32[bucket_count] // Array of hash values, one for each symbol starting at symbol_base. // values: U32[symbol_count] } /// Calculate the GNU hash for a symbol name. /// /// Used for `SHT_GNU_HASH`. pub fn gnu_hash(name: &[u8]) -> u32 { let mut hash = 5381u32; for byte in name { hash = hash.wrapping_mul(33).wrapping_add(u32::from(*byte)); } hash } // Motorola 68k specific definitions. // m68k values for `Rel*::r_type`. /// No reloc pub const R_68K_NONE: u32 = 0; /// Direct 32 bit pub const R_68K_32: u32 = 1; /// Direct 16 bit pub const R_68K_16: u32 = 2; /// Direct 8 bit pub const R_68K_8: u32 = 3; /// PC relative 32 bit pub const R_68K_PC32: u32 = 4; /// PC relative 16 bit pub const R_68K_PC16: u32 = 5; /// PC relative 8 bit pub const R_68K_PC8: u32 = 6; /// 32 bit PC relative GOT entry pub const R_68K_GOT32: u32 = 7; /// 16 bit PC relative GOT entry pub const R_68K_GOT16: u32 = 8; /// 8 bit PC relative GOT entry pub const R_68K_GOT8: u32 = 9; /// 32 bit GOT offset pub const R_68K_GOT32O: u32 = 10; /// 16 bit GOT offset pub const R_68K_GOT16O: u32 = 11; /// 8 bit GOT offset pub const R_68K_GOT8O: u32 = 12; /// 32 bit PC relative PLT address pub const R_68K_PLT32: u32 = 13; /// 16 bit PC relative PLT address pub const R_68K_PLT16: u32 = 14; /// 8 bit PC relative PLT address pub const R_68K_PLT8: u32 = 15; /// 32 bit PLT offset pub const R_68K_PLT32O: u32 = 16; /// 16 bit PLT offset pub const R_68K_PLT16O: u32 = 17; /// 8 bit PLT offset pub const R_68K_PLT8O: u32 = 18; /// Copy symbol at runtime pub const R_68K_COPY: u32 = 19; /// Create GOT entry pub const R_68K_GLOB_DAT: u32 = 20; /// Create PLT entry pub const R_68K_JMP_SLOT: u32 = 21; /// Adjust by program base pub const R_68K_RELATIVE: u32 = 22; /// 32 bit GOT offset for GD pub const R_68K_TLS_GD32: u32 = 25; /// 16 bit GOT offset for GD pub const R_68K_TLS_GD16: u32 = 26; /// 8 bit GOT offset for GD pub const R_68K_TLS_GD8: u32 = 27; /// 32 bit GOT offset for LDM pub const R_68K_TLS_LDM32: u32 = 28; /// 16 bit GOT offset for LDM pub const R_68K_TLS_LDM16: u32 = 29; /// 8 bit GOT offset for LDM pub const R_68K_TLS_LDM8: u32 = 30; /// 32 bit module-relative offset pub const R_68K_TLS_LDO32: u32 = 31; /// 16 bit module-relative offset pub const R_68K_TLS_LDO16: u32 = 32; /// 8 bit module-relative offset pub const R_68K_TLS_LDO8: u32 = 33; /// 32 bit GOT offset for IE pub const R_68K_TLS_IE32: u32 = 34; /// 16 bit GOT offset for IE pub const R_68K_TLS_IE16: u32 = 35; /// 8 bit GOT offset for IE pub const R_68K_TLS_IE8: u32 = 36; /// 32 bit offset relative to static TLS block pub const R_68K_TLS_LE32: u32 = 37; /// 16 bit offset relative to static TLS block pub const R_68K_TLS_LE16: u32 = 38; /// 8 bit offset relative to static TLS block pub const R_68K_TLS_LE8: u32 = 39; /// 32 bit module number pub const R_68K_TLS_DTPMOD32: u32 = 40; /// 32 bit module-relative offset pub const R_68K_TLS_DTPREL32: u32 = 41; /// 32 bit TP-relative offset pub const R_68K_TLS_TPREL32: u32 = 42; // Intel 80386 specific definitions. // i386 values for `Rel*::r_type`. /// No reloc pub const R_386_NONE: u32 = 0; /// Direct 32 bit pub const R_386_32: u32 = 1; /// PC relative 32 bit pub const R_386_PC32: u32 = 2; /// 32 bit GOT entry pub const R_386_GOT32: u32 = 3; /// 32 bit PLT address pub const R_386_PLT32: u32 = 4; /// Copy symbol at runtime pub const R_386_COPY: u32 = 5; /// Create GOT entry pub const R_386_GLOB_DAT: u32 = 6; /// Create PLT entry pub const R_386_JMP_SLOT: u32 = 7; /// Adjust by program base pub const R_386_RELATIVE: u32 = 8; /// 32 bit offset to GOT pub const R_386_GOTOFF: u32 = 9; /// 32 bit PC relative offset to GOT pub const R_386_GOTPC: u32 = 10; /// Direct 32 bit PLT address pub const R_386_32PLT: u32 = 11; /// Offset in static TLS block pub const R_386_TLS_TPOFF: u32 = 14; /// Address of GOT entry for static TLS block offset pub const R_386_TLS_IE: u32 = 15; /// GOT entry for static TLS block offset pub const R_386_TLS_GOTIE: u32 = 16; /// Offset relative to static TLS block pub const R_386_TLS_LE: u32 = 17; /// Direct 32 bit for GNU version of general dynamic thread local data pub const R_386_TLS_GD: u32 = 18; /// Direct 32 bit for GNU version of local dynamic thread local data in LE code pub const R_386_TLS_LDM: u32 = 19; /// Direct 16 bit pub const R_386_16: u32 = 20; /// PC relative 16 bit pub const R_386_PC16: u32 = 21; /// Direct 8 bit pub const R_386_8: u32 = 22; /// PC relative 8 bit pub const R_386_PC8: u32 = 23; /// Direct 32 bit for general dynamic thread local data pub const R_386_TLS_GD_32: u32 = 24; /// Tag for pushl in GD TLS code pub const R_386_TLS_GD_PUSH: u32 = 25; /// Relocation for call to __tls_get_addr() pub const R_386_TLS_GD_CALL: u32 = 26; /// Tag for popl in GD TLS code pub const R_386_TLS_GD_POP: u32 = 27; /// Direct 32 bit for local dynamic thread local data in LE code pub const R_386_TLS_LDM_32: u32 = 28; /// Tag for pushl in LDM TLS code pub const R_386_TLS_LDM_PUSH: u32 = 29; /// Relocation for call to __tls_get_addr() in LDM code pub const R_386_TLS_LDM_CALL: u32 = 30; /// Tag for popl in LDM TLS code pub const R_386_TLS_LDM_POP: u32 = 31; /// Offset relative to TLS block pub const R_386_TLS_LDO_32: u32 = 32; /// GOT entry for negated static TLS block offset pub const R_386_TLS_IE_32: u32 = 33; /// Negated offset relative to static TLS block pub const R_386_TLS_LE_32: u32 = 34; /// ID of module containing symbol pub const R_386_TLS_DTPMOD32: u32 = 35; /// Offset in TLS block pub const R_386_TLS_DTPOFF32: u32 = 36; /// Negated offset in static TLS block pub const R_386_TLS_TPOFF32: u32 = 37; /// 32-bit symbol size pub const R_386_SIZE32: u32 = 38; /// GOT offset for TLS descriptor. pub const R_386_TLS_GOTDESC: u32 = 39; /// Marker of call through TLS descriptor for relaxation. pub const R_386_TLS_DESC_CALL: u32 = 40; /// TLS descriptor containing pointer to code and to argument, returning the TLS offset for the symbol. pub const R_386_TLS_DESC: u32 = 41; /// Adjust indirectly by program base pub const R_386_IRELATIVE: u32 = 42; /// Load from 32 bit GOT entry, relaxable. pub const R_386_GOT32X: u32 = 43; // ADI SHARC specific definitions // SHARC values for `Rel*::r_type` /// 24-bit absolute address in bits 23:0 of a 48-bit instr /// /// Targets: /// /// * Type 25a (PC_DIRECT) pub const R_SHARC_ADDR24_V3: u32 = 0x0b; /// 32-bit absolute address in bits 31:0 of a 48-bit instr /// /// Targets: /// /// * Type 14a /// * Type 14d /// * Type 15a /// * Type 16a /// * Type 17a /// * Type 18a /// * Type 19a pub const R_SHARC_ADDR32_V3: u32 = 0x0c; /// 32-bit absolute address in bits 31:0 of a 32-bit data location /// /// Represented with `RelocationEncoding::Generic` pub const R_SHARC_ADDR_VAR_V3: u32 = 0x0d; /// 6-bit PC-relative address in bits 32:27 of a 48-bit instr /// /// Targets: /// /// * Type 9a /// * Type 10a pub const R_SHARC_PCRSHORT_V3: u32 = 0x0e; /// 24-bit PC-relative address in bits 23:0 of a 48-bit instr /// /// Targets: /// /// * Type 8a /// * Type 12a (truncated to 23 bits after relocation) /// * Type 13a (truncated to 23 bits after relocation) /// * Type 25a (PC Relative) pub const R_SHARC_PCRLONG_V3: u32 = 0x0f; /// 6-bit absolute address in bits 32:27 of a 48-bit instr /// /// Targets: /// /// * Type 4a /// * Type 4b /// * Type 4d pub const R_SHARC_DATA6_V3: u32 = 0x10; /// 16-bit absolute address in bits 39:24 of a 48-bit instr /// /// Targets: /// /// * Type 12a pub const R_SHARC_DATA16_V3: u32 = 0x11; /// 6-bit absolute address into bits 16:11 of a 32-bit instr /// /// Targets: /// /// * Type 4b pub const R_SHARC_DATA6_VISA_V3: u32 = 0x12; /// 7-bit absolute address into bits 6:0 of a 32-bit instr pub const R_SHARC_DATA7_VISA_V3: u32 = 0x13; /// 16-bit absolute address into bits 15:0 of a 32-bit instr pub const R_SHARC_DATA16_VISA_V3: u32 = 0x14; /// 6-bit PC-relative address into bits 16:11 of a Type B /// /// Targets: /// /// * Type 9b pub const R_SHARC_PCR6_VISA_V3: u32 = 0x17; /// 16-bit absolute address into bits 15:0 of a 16-bit location. /// /// Represented with `RelocationEncoding::Generic` pub const R_SHARC_ADDR_VAR16_V3: u32 = 0x19; pub const R_SHARC_CALC_PUSH_ADDR: u32 = 0xe0; pub const R_SHARC_CALC_PUSH_ADDEND: u32 = 0xe1; pub const R_SHARC_CALC_ADD: u32 = 0xe2; pub const R_SHARC_CALC_SUB: u32 = 0xe3; pub const R_SHARC_CALC_MUL: u32 = 0xe4; pub const R_SHARC_CALC_DIV: u32 = 0xe5; pub const R_SHARC_CALC_MOD: u32 = 0xe6; pub const R_SHARC_CALC_LSHIFT: u32 = 0xe7; pub const R_SHARC_CALC_RSHIFT: u32 = 0xe8; pub const R_SHARC_CALC_AND: u32 = 0xe9; pub const R_SHARC_CALC_OR: u32 = 0xea; pub const R_SHARC_CALC_XOR: u32 = 0xeb; pub const R_SHARC_CALC_PUSH_LEN: u32 = 0xec; pub const R_SHARC_CALC_NOT: u32 = 0xf6; // SHARC values for `SectionHeader*::sh_type`. /// .adi.attributes pub const SHT_SHARC_ADI_ATTRIBUTES: u32 = SHT_LOPROC + 0x2; // SUN SPARC specific definitions. // SPARC values for `st_type` component of `Sym*::st_info`. /// Global register reserved to app. pub const STT_SPARC_REGISTER: u8 = 13; // SPARC values for `FileHeader64::e_flags`. pub const EF_SPARCV9_MM: u32 = 3; pub const EF_SPARCV9_TSO: u32 = 0; pub const EF_SPARCV9_PSO: u32 = 1; pub const EF_SPARCV9_RMO: u32 = 2; /// little endian data pub const EF_SPARC_LEDATA: u32 = 0x80_0000; pub const EF_SPARC_EXT_MASK: u32 = 0xFF_FF00; /// generic V8+ features pub const EF_SPARC_32PLUS: u32 = 0x00_0100; /// Sun UltraSPARC1 extensions pub const EF_SPARC_SUN_US1: u32 = 0x00_0200; /// HAL R1 extensions pub const EF_SPARC_HAL_R1: u32 = 0x00_0400; /// Sun UltraSPARCIII extensions pub const EF_SPARC_SUN_US3: u32 = 0x00_0800; // SPARC values for `Rel*::r_type`. /// No reloc pub const R_SPARC_NONE: u32 = 0; /// Direct 8 bit pub const R_SPARC_8: u32 = 1; /// Direct 16 bit pub const R_SPARC_16: u32 = 2; /// Direct 32 bit pub const R_SPARC_32: u32 = 3; /// PC relative 8 bit pub const R_SPARC_DISP8: u32 = 4; /// PC relative 16 bit pub const R_SPARC_DISP16: u32 = 5; /// PC relative 32 bit pub const R_SPARC_DISP32: u32 = 6; /// PC relative 30 bit shifted pub const R_SPARC_WDISP30: u32 = 7; /// PC relative 22 bit shifted pub const R_SPARC_WDISP22: u32 = 8; /// High 22 bit pub const R_SPARC_HI22: u32 = 9; /// Direct 22 bit pub const R_SPARC_22: u32 = 10; /// Direct 13 bit pub const R_SPARC_13: u32 = 11; /// Truncated 10 bit pub const R_SPARC_LO10: u32 = 12; /// Truncated 10 bit GOT entry pub const R_SPARC_GOT10: u32 = 13; /// 13 bit GOT entry pub const R_SPARC_GOT13: u32 = 14; /// 22 bit GOT entry shifted pub const R_SPARC_GOT22: u32 = 15; /// PC relative 10 bit truncated pub const R_SPARC_PC10: u32 = 16; /// PC relative 22 bit shifted pub const R_SPARC_PC22: u32 = 17; /// 30 bit PC relative PLT address pub const R_SPARC_WPLT30: u32 = 18; /// Copy symbol at runtime pub const R_SPARC_COPY: u32 = 19; /// Create GOT entry pub const R_SPARC_GLOB_DAT: u32 = 20; /// Create PLT entry pub const R_SPARC_JMP_SLOT: u32 = 21; /// Adjust by program base pub const R_SPARC_RELATIVE: u32 = 22; /// Direct 32 bit unaligned pub const R_SPARC_UA32: u32 = 23; // Sparc64 values for `Rel*::r_type`. /// Direct 32 bit ref to PLT entry pub const R_SPARC_PLT32: u32 = 24; /// High 22 bit PLT entry pub const R_SPARC_HIPLT22: u32 = 25; /// Truncated 10 bit PLT entry pub const R_SPARC_LOPLT10: u32 = 26; /// PC rel 32 bit ref to PLT entry pub const R_SPARC_PCPLT32: u32 = 27; /// PC rel high 22 bit PLT entry pub const R_SPARC_PCPLT22: u32 = 28; /// PC rel trunc 10 bit PLT entry pub const R_SPARC_PCPLT10: u32 = 29; /// Direct 10 bit pub const R_SPARC_10: u32 = 30; /// Direct 11 bit pub const R_SPARC_11: u32 = 31; /// Direct 64 bit pub const R_SPARC_64: u32 = 32; /// 10bit with secondary 13bit addend pub const R_SPARC_OLO10: u32 = 33; /// Top 22 bits of direct 64 bit pub const R_SPARC_HH22: u32 = 34; /// High middle 10 bits of ... pub const R_SPARC_HM10: u32 = 35; /// Low middle 22 bits of ... pub const R_SPARC_LM22: u32 = 36; /// Top 22 bits of pc rel 64 bit pub const R_SPARC_PC_HH22: u32 = 37; /// High middle 10 bit of ... pub const R_SPARC_PC_HM10: u32 = 38; /// Low miggle 22 bits of ... pub const R_SPARC_PC_LM22: u32 = 39; /// PC relative 16 bit shifted pub const R_SPARC_WDISP16: u32 = 40; /// PC relative 19 bit shifted pub const R_SPARC_WDISP19: u32 = 41; /// was part of v9 ABI but was removed pub const R_SPARC_GLOB_JMP: u32 = 42; /// Direct 7 bit pub const R_SPARC_7: u32 = 43; /// Direct 5 bit pub const R_SPARC_5: u32 = 44; /// Direct 6 bit pub const R_SPARC_6: u32 = 45; /// PC relative 64 bit pub const R_SPARC_DISP64: u32 = 46; /// Direct 64 bit ref to PLT entry pub const R_SPARC_PLT64: u32 = 47; /// High 22 bit complemented pub const R_SPARC_HIX22: u32 = 48; /// Truncated 11 bit complemented pub const R_SPARC_LOX10: u32 = 49; /// Direct high 12 of 44 bit pub const R_SPARC_H44: u32 = 50; /// Direct mid 22 of 44 bit pub const R_SPARC_M44: u32 = 51; /// Direct low 10 of 44 bit pub const R_SPARC_L44: u32 = 52; /// Global register usage pub const R_SPARC_REGISTER: u32 = 53; /// Direct 64 bit unaligned pub const R_SPARC_UA64: u32 = 54; /// Direct 16 bit unaligned pub const R_SPARC_UA16: u32 = 55; pub const R_SPARC_TLS_GD_HI22: u32 = 56; pub const R_SPARC_TLS_GD_LO10: u32 = 57; pub const R_SPARC_TLS_GD_ADD: u32 = 58; pub const R_SPARC_TLS_GD_CALL: u32 = 59; pub const R_SPARC_TLS_LDM_HI22: u32 = 60; pub const R_SPARC_TLS_LDM_LO10: u32 = 61; pub const R_SPARC_TLS_LDM_ADD: u32 = 62; pub const R_SPARC_TLS_LDM_CALL: u32 = 63; pub const R_SPARC_TLS_LDO_HIX22: u32 = 64; pub const R_SPARC_TLS_LDO_LOX10: u32 = 65; pub const R_SPARC_TLS_LDO_ADD: u32 = 66; pub const R_SPARC_TLS_IE_HI22: u32 = 67; pub const R_SPARC_TLS_IE_LO10: u32 = 68; pub const R_SPARC_TLS_IE_LD: u32 = 69; pub const R_SPARC_TLS_IE_LDX: u32 = 70; pub const R_SPARC_TLS_IE_ADD: u32 = 71; pub const R_SPARC_TLS_LE_HIX22: u32 = 72; pub const R_SPARC_TLS_LE_LOX10: u32 = 73; pub const R_SPARC_TLS_DTPMOD32: u32 = 74; pub const R_SPARC_TLS_DTPMOD64: u32 = 75; pub const R_SPARC_TLS_DTPOFF32: u32 = 76; pub const R_SPARC_TLS_DTPOFF64: u32 = 77; pub const R_SPARC_TLS_TPOFF32: u32 = 78; pub const R_SPARC_TLS_TPOFF64: u32 = 79; pub const R_SPARC_GOTDATA_HIX22: u32 = 80; pub const R_SPARC_GOTDATA_LOX10: u32 = 81; pub const R_SPARC_GOTDATA_OP_HIX22: u32 = 82; pub const R_SPARC_GOTDATA_OP_LOX10: u32 = 83; pub const R_SPARC_GOTDATA_OP: u32 = 84; pub const R_SPARC_H34: u32 = 85; pub const R_SPARC_SIZE32: u32 = 86; pub const R_SPARC_SIZE64: u32 = 87; pub const R_SPARC_WDISP10: u32 = 88; pub const R_SPARC_JMP_IREL: u32 = 248; pub const R_SPARC_IRELATIVE: u32 = 249; pub const R_SPARC_GNU_VTINHERIT: u32 = 250; pub const R_SPARC_GNU_VTENTRY: u32 = 251; pub const R_SPARC_REV32: u32 = 252; // Sparc64 values for `Dyn32::d_tag`. pub const DT_SPARC_REGISTER: u32 = 0x7000_0001; // MIPS R3000 specific definitions. // MIPS values for `FileHeader32::e_flags`. /// A .noreorder directive was used. pub const EF_MIPS_NOREORDER: u32 = 1; /// Contains PIC code. pub const EF_MIPS_PIC: u32 = 2; /// Uses PIC calling sequence. pub const EF_MIPS_CPIC: u32 = 4; pub const EF_MIPS_XGOT: u32 = 8; pub const EF_MIPS_64BIT_WHIRL: u32 = 16; pub const EF_MIPS_ABI2: u32 = 32; pub const EF_MIPS_ABI_ON32: u32 = 64; /// Uses FP64 (12 callee-saved). pub const EF_MIPS_FP64: u32 = 512; /// Uses IEEE 754-2008 NaN encoding. pub const EF_MIPS_NAN2008: u32 = 1024; /// MIPS architecture level. pub const EF_MIPS_ARCH: u32 = 0xf000_0000; /// The first MIPS 32 bit ABI pub const EF_MIPS_ABI_O32: u32 = 0x0000_1000; /// O32 ABI extended for 64-bit architectures pub const EF_MIPS_ABI_O64: u32 = 0x0000_2000; /// EABI in 32-bit mode pub const EF_MIPS_ABI_EABI32: u32 = 0x0000_3000; /// EABI in 64-bit mode pub const EF_MIPS_ABI_EABI64: u32 = 0x0000_4000; /// Mask for selecting EF_MIPS_ABI_ variant pub const EF_MIPS_ABI: u32 = 0x0000_f000; // Legal values for MIPS architecture level. /// -mips1 code. pub const EF_MIPS_ARCH_1: u32 = 0x0000_0000; /// -mips2 code. pub const EF_MIPS_ARCH_2: u32 = 0x1000_0000; /// -mips3 code. pub const EF_MIPS_ARCH_3: u32 = 0x2000_0000; /// -mips4 code. pub const EF_MIPS_ARCH_4: u32 = 0x3000_0000; /// -mips5 code. pub const EF_MIPS_ARCH_5: u32 = 0x4000_0000; /// MIPS32 code. pub const EF_MIPS_ARCH_32: u32 = 0x5000_0000; /// MIPS64 code. pub const EF_MIPS_ARCH_64: u32 = 0x6000_0000; /// MIPS32r2 code. pub const EF_MIPS_ARCH_32R2: u32 = 0x7000_0000; /// MIPS64r2 code. pub const EF_MIPS_ARCH_64R2: u32 = 0x8000_0000; /// MIPS32r6 code pub const EF_MIPS_ARCH_32R6: u32 = 0x9000_0000; /// MIPS64r6 code pub const EF_MIPS_ARCH_64R6: u32 = 0xa000_0000; // MIPS values for `Sym32::st_shndx`. /// Allocated common symbols. pub const SHN_MIPS_ACOMMON: u16 = 0xff00; /// Allocated test symbols. pub const SHN_MIPS_TEXT: u16 = 0xff01; /// Allocated data symbols. pub const SHN_MIPS_DATA: u16 = 0xff02; /// Small common symbols. pub const SHN_MIPS_SCOMMON: u16 = 0xff03; /// Small undefined symbols. pub const SHN_MIPS_SUNDEFINED: u16 = 0xff04; // MIPS values for `SectionHeader32::sh_type`. /// Shared objects used in link. pub const SHT_MIPS_LIBLIST: u32 = 0x7000_0000; pub const SHT_MIPS_MSYM: u32 = 0x7000_0001; /// Conflicting symbols. pub const SHT_MIPS_CONFLICT: u32 = 0x7000_0002; /// Global data area sizes. pub const SHT_MIPS_GPTAB: u32 = 0x7000_0003; /// Reserved for SGI/MIPS compilers pub const SHT_MIPS_UCODE: u32 = 0x7000_0004; /// MIPS ECOFF debugging info. pub const SHT_MIPS_DEBUG: u32 = 0x7000_0005; /// Register usage information. pub const SHT_MIPS_REGINFO: u32 = 0x7000_0006; pub const SHT_MIPS_PACKAGE: u32 = 0x7000_0007; pub const SHT_MIPS_PACKSYM: u32 = 0x7000_0008; pub const SHT_MIPS_RELD: u32 = 0x7000_0009; pub const SHT_MIPS_IFACE: u32 = 0x7000_000b; pub const SHT_MIPS_CONTENT: u32 = 0x7000_000c; /// Miscellaneous options. pub const SHT_MIPS_OPTIONS: u32 = 0x7000_000d; pub const SHT_MIPS_SHDR: u32 = 0x7000_0010; pub const SHT_MIPS_FDESC: u32 = 0x7000_0011; pub const SHT_MIPS_EXTSYM: u32 = 0x7000_0012; pub const SHT_MIPS_DENSE: u32 = 0x7000_0013; pub const SHT_MIPS_PDESC: u32 = 0x7000_0014; pub const SHT_MIPS_LOCSYM: u32 = 0x7000_0015; pub const SHT_MIPS_AUXSYM: u32 = 0x7000_0016; pub const SHT_MIPS_OPTSYM: u32 = 0x7000_0017; pub const SHT_MIPS_LOCSTR: u32 = 0x7000_0018; pub const SHT_MIPS_LINE: u32 = 0x7000_0019; pub const SHT_MIPS_RFDESC: u32 = 0x7000_001a; pub const SHT_MIPS_DELTASYM: u32 = 0x7000_001b; pub const SHT_MIPS_DELTAINST: u32 = 0x7000_001c; pub const SHT_MIPS_DELTACLASS: u32 = 0x7000_001d; /// DWARF debugging information. pub const SHT_MIPS_DWARF: u32 = 0x7000_001e; pub const SHT_MIPS_DELTADECL: u32 = 0x7000_001f; pub const SHT_MIPS_SYMBOL_LIB: u32 = 0x7000_0020; /// Event section. pub const SHT_MIPS_EVENTS: u32 = 0x7000_0021; pub const SHT_MIPS_TRANSLATE: u32 = 0x7000_0022; pub const SHT_MIPS_PIXIE: u32 = 0x7000_0023; pub const SHT_MIPS_XLATE: u32 = 0x7000_0024; pub const SHT_MIPS_XLATE_DEBUG: u32 = 0x7000_0025; pub const SHT_MIPS_WHIRL: u32 = 0x7000_0026; pub const SHT_MIPS_EH_REGION: u32 = 0x7000_0027; pub const SHT_MIPS_XLATE_OLD: u32 = 0x7000_0028; pub const SHT_MIPS_PDR_EXCEPTION: u32 = 0x7000_0029; // MIPS values for `SectionHeader32::sh_flags`. /// Must be in global data area. pub const SHF_MIPS_GPREL: u32 = 0x1000_0000; pub const SHF_MIPS_MERGE: u32 = 0x2000_0000; pub const SHF_MIPS_ADDR: u32 = 0x4000_0000; pub const SHF_MIPS_STRINGS: u32 = 0x8000_0000; pub const SHF_MIPS_NOSTRIP: u32 = 0x0800_0000; pub const SHF_MIPS_LOCAL: u32 = 0x0400_0000; pub const SHF_MIPS_NAMES: u32 = 0x0200_0000; pub const SHF_MIPS_NODUPE: u32 = 0x0100_0000; // MIPS values for `Sym32::st_other`. pub const STO_MIPS_PLT: u8 = 0x8; /// Only valid for `STB_MIPS_SPLIT_COMMON`. pub const STO_MIPS_SC_ALIGN_UNUSED: u8 = 0xff; // MIPS values for `Sym32::st_info'. pub const STB_MIPS_SPLIT_COMMON: u8 = 13; // Entries found in sections of type `SHT_MIPS_GPTAB`. // TODO: Elf32_gptab, Elf32_RegInfo, Elf_Options // Values for `Elf_Options::kind`. /// Undefined. pub const ODK_NULL: u32 = 0; /// Register usage information. pub const ODK_REGINFO: u32 = 1; /// Exception processing options. pub const ODK_EXCEPTIONS: u32 = 2; /// Section padding options. pub const ODK_PAD: u32 = 3; /// Hardware workarounds performed pub const ODK_HWPATCH: u32 = 4; /// record the fill value used by the linker. pub const ODK_FILL: u32 = 5; /// reserve space for desktop tools to write. pub const ODK_TAGS: u32 = 6; /// HW workarounds. 'AND' bits when merging. pub const ODK_HWAND: u32 = 7; /// HW workarounds. 'OR' bits when merging. pub const ODK_HWOR: u32 = 8; // Values for `Elf_Options::info` for `ODK_EXCEPTIONS` entries. /// FPE's which MUST be enabled. pub const OEX_FPU_MIN: u32 = 0x1f; /// FPE's which MAY be enabled. pub const OEX_FPU_MAX: u32 = 0x1f00; /// page zero must be mapped. pub const OEX_PAGE0: u32 = 0x10000; /// Force sequential memory mode? pub const OEX_SMM: u32 = 0x20000; /// Force floating point debug mode? pub const OEX_FPDBUG: u32 = 0x40000; pub const OEX_PRECISEFP: u32 = OEX_FPDBUG; /// Dismiss invalid address faults? pub const OEX_DISMISS: u32 = 0x80000; pub const OEX_FPU_INVAL: u32 = 0x10; pub const OEX_FPU_DIV0: u32 = 0x08; pub const OEX_FPU_OFLO: u32 = 0x04; pub const OEX_FPU_UFLO: u32 = 0x02; pub const OEX_FPU_INEX: u32 = 0x01; // Masks for `Elf_Options::info` for an `ODK_HWPATCH` entry. */ /// R4000 end-of-page patch. pub const OHW_R4KEOP: u32 = 0x1; /// may need R8000 prefetch patch. pub const OHW_R8KPFETCH: u32 = 0x2; /// R5000 end-of-page patch. pub const OHW_R5KEOP: u32 = 0x4; /// R5000 cvt.\[ds\].l bug. clean=1. pub const OHW_R5KCVTL: u32 = 0x8; pub const OPAD_PREFIX: u32 = 0x1; pub const OPAD_POSTFIX: u32 = 0x2; pub const OPAD_SYMBOL: u32 = 0x4; // Entries found in sections of type `SHT_MIPS_OPTIONS`. // TODO: Elf_Options_Hw // Masks for `ElfOptions::info` for `ODK_HWAND` and `ODK_HWOR` entries. pub const OHWA0_R4KEOP_CHECKED: u32 = 0x0000_0001; pub const OHWA1_R4KEOP_CLEAN: u32 = 0x0000_0002; // MIPS values for `Rel*::r_type`. /// No reloc pub const R_MIPS_NONE: u32 = 0; /// Direct 16 bit pub const R_MIPS_16: u32 = 1; /// Direct 32 bit pub const R_MIPS_32: u32 = 2; /// PC relative 32 bit pub const R_MIPS_REL32: u32 = 3; /// Direct 26 bit shifted pub const R_MIPS_26: u32 = 4; /// High 16 bit pub const R_MIPS_HI16: u32 = 5; /// Low 16 bit pub const R_MIPS_LO16: u32 = 6; /// GP relative 16 bit pub const R_MIPS_GPREL16: u32 = 7; /// 16 bit literal entry pub const R_MIPS_LITERAL: u32 = 8; /// 16 bit GOT entry pub const R_MIPS_GOT16: u32 = 9; /// PC relative 16 bit pub const R_MIPS_PC16: u32 = 10; /// 16 bit GOT entry for function pub const R_MIPS_CALL16: u32 = 11; /// GP relative 32 bit pub const R_MIPS_GPREL32: u32 = 12; pub const R_MIPS_SHIFT5: u32 = 16; pub const R_MIPS_SHIFT6: u32 = 17; pub const R_MIPS_64: u32 = 18; pub const R_MIPS_GOT_DISP: u32 = 19; pub const R_MIPS_GOT_PAGE: u32 = 20; pub const R_MIPS_GOT_OFST: u32 = 21; pub const R_MIPS_GOT_HI16: u32 = 22; pub const R_MIPS_GOT_LO16: u32 = 23; pub const R_MIPS_SUB: u32 = 24; pub const R_MIPS_INSERT_A: u32 = 25; pub const R_MIPS_INSERT_B: u32 = 26; pub const R_MIPS_DELETE: u32 = 27; pub const R_MIPS_HIGHER: u32 = 28; pub const R_MIPS_HIGHEST: u32 = 29; pub const R_MIPS_CALL_HI16: u32 = 30; pub const R_MIPS_CALL_LO16: u32 = 31; pub const R_MIPS_SCN_DISP: u32 = 32; pub const R_MIPS_REL16: u32 = 33; pub const R_MIPS_ADD_IMMEDIATE: u32 = 34; pub const R_MIPS_PJUMP: u32 = 35; pub const R_MIPS_RELGOT: u32 = 36; pub const R_MIPS_JALR: u32 = 37; /// Module number 32 bit pub const R_MIPS_TLS_DTPMOD32: u32 = 38; /// Module-relative offset 32 bit pub const R_MIPS_TLS_DTPREL32: u32 = 39; /// Module number 64 bit pub const R_MIPS_TLS_DTPMOD64: u32 = 40; /// Module-relative offset 64 bit pub const R_MIPS_TLS_DTPREL64: u32 = 41; /// 16 bit GOT offset for GD pub const R_MIPS_TLS_GD: u32 = 42; /// 16 bit GOT offset for LDM pub const R_MIPS_TLS_LDM: u32 = 43; /// Module-relative offset, high 16 bits pub const R_MIPS_TLS_DTPREL_HI16: u32 = 44; /// Module-relative offset, low 16 bits pub const R_MIPS_TLS_DTPREL_LO16: u32 = 45; /// 16 bit GOT offset for IE pub const R_MIPS_TLS_GOTTPREL: u32 = 46; /// TP-relative offset, 32 bit pub const R_MIPS_TLS_TPREL32: u32 = 47; /// TP-relative offset, 64 bit pub const R_MIPS_TLS_TPREL64: u32 = 48; /// TP-relative offset, high 16 bits pub const R_MIPS_TLS_TPREL_HI16: u32 = 49; /// TP-relative offset, low 16 bits pub const R_MIPS_TLS_TPREL_LO16: u32 = 50; pub const R_MIPS_GLOB_DAT: u32 = 51; pub const R_MIPS_COPY: u32 = 126; pub const R_MIPS_JUMP_SLOT: u32 = 127; // MIPS values for `ProgramHeader32::p_type`. /// Register usage information. pub const PT_MIPS_REGINFO: u32 = 0x7000_0000; /// Runtime procedure table. pub const PT_MIPS_RTPROC: u32 = 0x7000_0001; pub const PT_MIPS_OPTIONS: u32 = 0x7000_0002; /// FP mode requirement. pub const PT_MIPS_ABIFLAGS: u32 = 0x7000_0003; // MIPS values for `ProgramHeader32::p_flags`. pub const PF_MIPS_LOCAL: u32 = 0x1000_0000; // MIPS values for `Dyn32::d_tag`. /// Runtime linker interface version pub const DT_MIPS_RLD_VERSION: u32 = 0x7000_0001; /// Timestamp pub const DT_MIPS_TIME_STAMP: u32 = 0x7000_0002; /// Checksum pub const DT_MIPS_ICHECKSUM: u32 = 0x7000_0003; /// Version string (string tbl index) pub const DT_MIPS_IVERSION: u32 = 0x7000_0004; /// Flags pub const DT_MIPS_FLAGS: u32 = 0x7000_0005; /// Base address pub const DT_MIPS_BASE_ADDRESS: u32 = 0x7000_0006; pub const DT_MIPS_MSYM: u32 = 0x7000_0007; /// Address of CONFLICT section pub const DT_MIPS_CONFLICT: u32 = 0x7000_0008; /// Address of LIBLIST section pub const DT_MIPS_LIBLIST: u32 = 0x7000_0009; /// Number of local GOT entries pub const DT_MIPS_LOCAL_GOTNO: u32 = 0x7000_000a; /// Number of CONFLICT entries pub const DT_MIPS_CONFLICTNO: u32 = 0x7000_000b; /// Number of LIBLIST entries pub const DT_MIPS_LIBLISTNO: u32 = 0x7000_0010; /// Number of DYNSYM entries pub const DT_MIPS_SYMTABNO: u32 = 0x7000_0011; /// First external DYNSYM pub const DT_MIPS_UNREFEXTNO: u32 = 0x7000_0012; /// First GOT entry in DYNSYM pub const DT_MIPS_GOTSYM: u32 = 0x7000_0013; /// Number of GOT page table entries pub const DT_MIPS_HIPAGENO: u32 = 0x7000_0014; /// Address of run time loader map. pub const DT_MIPS_RLD_MAP: u32 = 0x7000_0016; /// Delta C++ class definition. pub const DT_MIPS_DELTA_CLASS: u32 = 0x7000_0017; /// Number of entries in DT_MIPS_DELTA_CLASS. pub const DT_MIPS_DELTA_CLASS_NO: u32 = 0x7000_0018; /// Delta C++ class instances. pub const DT_MIPS_DELTA_INSTANCE: u32 = 0x7000_0019; /// Number of entries in DT_MIPS_DELTA_INSTANCE. pub const DT_MIPS_DELTA_INSTANCE_NO: u32 = 0x7000_001a; /// Delta relocations. pub const DT_MIPS_DELTA_RELOC: u32 = 0x7000_001b; /// Number of entries in DT_MIPS_DELTA_RELOC. pub const DT_MIPS_DELTA_RELOC_NO: u32 = 0x7000_001c; /// Delta symbols that Delta relocations refer to. pub const DT_MIPS_DELTA_SYM: u32 = 0x7000_001d; /// Number of entries in DT_MIPS_DELTA_SYM. pub const DT_MIPS_DELTA_SYM_NO: u32 = 0x7000_001e; /// Delta symbols that hold the class declaration. pub const DT_MIPS_DELTA_CLASSSYM: u32 = 0x7000_0020; /// Number of entries in DT_MIPS_DELTA_CLASSSYM. pub const DT_MIPS_DELTA_CLASSSYM_NO: u32 = 0x7000_0021; /// Flags indicating for C++ flavor. pub const DT_MIPS_CXX_FLAGS: u32 = 0x7000_0022; pub const DT_MIPS_PIXIE_INIT: u32 = 0x7000_0023; pub const DT_MIPS_SYMBOL_LIB: u32 = 0x7000_0024; pub const DT_MIPS_LOCALPAGE_GOTIDX: u32 = 0x7000_0025; pub const DT_MIPS_LOCAL_GOTIDX: u32 = 0x7000_0026; pub const DT_MIPS_HIDDEN_GOTIDX: u32 = 0x7000_0027; pub const DT_MIPS_PROTECTED_GOTIDX: u32 = 0x7000_0028; /// Address of .options. pub const DT_MIPS_OPTIONS: u32 = 0x7000_0029; /// Address of .interface. pub const DT_MIPS_INTERFACE: u32 = 0x7000_002a; pub const DT_MIPS_DYNSTR_ALIGN: u32 = 0x7000_002b; /// Size of the .interface section. pub const DT_MIPS_INTERFACE_SIZE: u32 = 0x7000_002c; /// Address of rld_text_rsolve function stored in GOT. pub const DT_MIPS_RLD_TEXT_RESOLVE_ADDR: u32 = 0x7000_002d; /// Default suffix of dso to be added by rld on dlopen() calls. pub const DT_MIPS_PERF_SUFFIX: u32 = 0x7000_002e; /// (O32)Size of compact rel section. pub const DT_MIPS_COMPACT_SIZE: u32 = 0x7000_002f; /// GP value for aux GOTs. pub const DT_MIPS_GP_VALUE: u32 = 0x7000_0030; /// Address of aux .dynamic. pub const DT_MIPS_AUX_DYNAMIC: u32 = 0x7000_0031; /// The address of .got.plt in an executable using the new non-PIC ABI. pub const DT_MIPS_PLTGOT: u32 = 0x7000_0032; /// The base of the PLT in an executable using the new non-PIC ABI if that PLT is writable. For a non-writable PLT, this is omitted or has a zero value. pub const DT_MIPS_RWPLT: u32 = 0x7000_0034; /// An alternative description of the classic MIPS RLD_MAP that is usable in a PIE as it stores a relative offset from the address of the tag rather than an absolute address. pub const DT_MIPS_RLD_MAP_REL: u32 = 0x7000_0035; // Values for `DT_MIPS_FLAGS` `Dyn32` entry. /// No flags pub const RHF_NONE: u32 = 0; /// Use quickstart pub const RHF_QUICKSTART: u32 = 1 << 0; /// Hash size not power of 2 pub const RHF_NOTPOT: u32 = 1 << 1; /// Ignore LD_LIBRARY_PATH pub const RHF_NO_LIBRARY_REPLACEMENT: u32 = 1 << 2; pub const RHF_NO_MOVE: u32 = 1 << 3; pub const RHF_SGI_ONLY: u32 = 1 << 4; pub const RHF_GUARANTEE_INIT: u32 = 1 << 5; pub const RHF_DELTA_C_PLUS_PLUS: u32 = 1 << 6; pub const RHF_GUARANTEE_START_INIT: u32 = 1 << 7; pub const RHF_PIXIE: u32 = 1 << 8; pub const RHF_DEFAULT_DELAY_LOAD: u32 = 1 << 9; pub const RHF_REQUICKSTART: u32 = 1 << 10; pub const RHF_REQUICKSTARTED: u32 = 1 << 11; pub const RHF_CORD: u32 = 1 << 12; pub const RHF_NO_UNRES_UNDEF: u32 = 1 << 13; pub const RHF_RLD_ORDER_SAFE: u32 = 1 << 14; // Entries found in sections of type `SHT_MIPS_LIBLIST`. // TODO: Elf32_Lib, Elf64_Lib // Values for `Lib*::l_flags`. pub const LL_NONE: u32 = 0; /// Require exact match pub const LL_EXACT_MATCH: u32 = 1 << 0; /// Ignore interface version pub const LL_IGNORE_INT_VER: u32 = 1 << 1; pub const LL_REQUIRE_MINOR: u32 = 1 << 2; pub const LL_EXPORTS: u32 = 1 << 3; pub const LL_DELAY_LOAD: u32 = 1 << 4; pub const LL_DELTA: u32 = 1 << 5; // TODO: MIPS ABI flags // PA-RISC specific definitions. // PA-RISC values for `FileHeader32::e_flags`. /// Trap nil pointer dereference. pub const EF_PARISC_TRAPNIL: u32 = 0x0001_0000; /// Program uses arch. extensions. pub const EF_PARISC_EXT: u32 = 0x0002_0000; /// Program expects little endian. pub const EF_PARISC_LSB: u32 = 0x0004_0000; /// Program expects wide mode. pub const EF_PARISC_WIDE: u32 = 0x0008_0000; /// No kernel assisted branch prediction. pub const EF_PARISC_NO_KABP: u32 = 0x0010_0000; /// Allow lazy swapping. pub const EF_PARISC_LAZYSWAP: u32 = 0x0040_0000; /// Architecture version. pub const EF_PARISC_ARCH: u32 = 0x0000_ffff; // Values for `EF_PARISC_ARCH'. /// PA-RISC 1.0 big-endian. pub const EFA_PARISC_1_0: u32 = 0x020b; /// PA-RISC 1.1 big-endian. pub const EFA_PARISC_1_1: u32 = 0x0210; /// PA-RISC 2.0 big-endian. pub const EFA_PARISC_2_0: u32 = 0x0214; // PA-RISC values for `Sym*::st_shndx`. /// Section for tentatively declared symbols in ANSI C. pub const SHN_PARISC_ANSI_COMMON: u16 = 0xff00; /// Common blocks in huge model. pub const SHN_PARISC_HUGE_COMMON: u16 = 0xff01; // PA-RISC values for `SectionHeader32::sh_type`. /// Contains product specific ext. pub const SHT_PARISC_EXT: u32 = 0x7000_0000; /// Unwind information. pub const SHT_PARISC_UNWIND: u32 = 0x7000_0001; /// Debug info for optimized code. pub const SHT_PARISC_DOC: u32 = 0x7000_0002; // PA-RISC values for `SectionHeader32::sh_flags`. /// Section with short addressing. pub const SHF_PARISC_SHORT: u32 = 0x2000_0000; /// Section far from gp. pub const SHF_PARISC_HUGE: u32 = 0x4000_0000; /// Static branch prediction code. pub const SHF_PARISC_SBP: u32 = 0x8000_0000; // PA-RISC values for `st_type` component of `Sym32::st_info`. /// Millicode function entry point. pub const STT_PARISC_MILLICODE: u8 = 13; pub const STT_HP_OPAQUE: u8 = STT_LOOS + 0x1; pub const STT_HP_STUB: u8 = STT_LOOS + 0x2; // PA-RISC values for `Rel*::r_type`. /// No reloc. pub const R_PARISC_NONE: u32 = 0; /// Direct 32-bit reference. pub const R_PARISC_DIR32: u32 = 1; /// Left 21 bits of eff. address. pub const R_PARISC_DIR21L: u32 = 2; /// Right 17 bits of eff. address. pub const R_PARISC_DIR17R: u32 = 3; /// 17 bits of eff. address. pub const R_PARISC_DIR17F: u32 = 4; /// Right 14 bits of eff. address. pub const R_PARISC_DIR14R: u32 = 6; /// 32-bit rel. address. pub const R_PARISC_PCREL32: u32 = 9; /// Left 21 bits of rel. address. pub const R_PARISC_PCREL21L: u32 = 10; /// Right 17 bits of rel. address. pub const R_PARISC_PCREL17R: u32 = 11; /// 17 bits of rel. address. pub const R_PARISC_PCREL17F: u32 = 12; /// Right 14 bits of rel. address. pub const R_PARISC_PCREL14R: u32 = 14; /// Left 21 bits of rel. address. pub const R_PARISC_DPREL21L: u32 = 18; /// Right 14 bits of rel. address. pub const R_PARISC_DPREL14R: u32 = 22; /// GP-relative, left 21 bits. pub const R_PARISC_GPREL21L: u32 = 26; /// GP-relative, right 14 bits. pub const R_PARISC_GPREL14R: u32 = 30; /// LT-relative, left 21 bits. pub const R_PARISC_LTOFF21L: u32 = 34; /// LT-relative, right 14 bits. pub const R_PARISC_LTOFF14R: u32 = 38; /// 32 bits section rel. address. pub const R_PARISC_SECREL32: u32 = 41; /// No relocation, set segment base. pub const R_PARISC_SEGBASE: u32 = 48; /// 32 bits segment rel. address. pub const R_PARISC_SEGREL32: u32 = 49; /// PLT rel. address, left 21 bits. pub const R_PARISC_PLTOFF21L: u32 = 50; /// PLT rel. address, right 14 bits. pub const R_PARISC_PLTOFF14R: u32 = 54; /// 32 bits LT-rel. function pointer. pub const R_PARISC_LTOFF_FPTR32: u32 = 57; /// LT-rel. fct ptr, left 21 bits. pub const R_PARISC_LTOFF_FPTR21L: u32 = 58; /// LT-rel. fct ptr, right 14 bits. pub const R_PARISC_LTOFF_FPTR14R: u32 = 62; /// 64 bits function address. pub const R_PARISC_FPTR64: u32 = 64; /// 32 bits function address. pub const R_PARISC_PLABEL32: u32 = 65; /// Left 21 bits of fdesc address. pub const R_PARISC_PLABEL21L: u32 = 66; /// Right 14 bits of fdesc address. pub const R_PARISC_PLABEL14R: u32 = 70; /// 64 bits PC-rel. address. pub const R_PARISC_PCREL64: u32 = 72; /// 22 bits PC-rel. address. pub const R_PARISC_PCREL22F: u32 = 74; /// PC-rel. address, right 14 bits. pub const R_PARISC_PCREL14WR: u32 = 75; /// PC rel. address, right 14 bits. pub const R_PARISC_PCREL14DR: u32 = 76; /// 16 bits PC-rel. address. pub const R_PARISC_PCREL16F: u32 = 77; /// 16 bits PC-rel. address. pub const R_PARISC_PCREL16WF: u32 = 78; /// 16 bits PC-rel. address. pub const R_PARISC_PCREL16DF: u32 = 79; /// 64 bits of eff. address. pub const R_PARISC_DIR64: u32 = 80; /// 14 bits of eff. address. pub const R_PARISC_DIR14WR: u32 = 83; /// 14 bits of eff. address. pub const R_PARISC_DIR14DR: u32 = 84; /// 16 bits of eff. address. pub const R_PARISC_DIR16F: u32 = 85; /// 16 bits of eff. address. pub const R_PARISC_DIR16WF: u32 = 86; /// 16 bits of eff. address. pub const R_PARISC_DIR16DF: u32 = 87; /// 64 bits of GP-rel. address. pub const R_PARISC_GPREL64: u32 = 88; /// GP-rel. address, right 14 bits. pub const R_PARISC_GPREL14WR: u32 = 91; /// GP-rel. address, right 14 bits. pub const R_PARISC_GPREL14DR: u32 = 92; /// 16 bits GP-rel. address. pub const R_PARISC_GPREL16F: u32 = 93; /// 16 bits GP-rel. address. pub const R_PARISC_GPREL16WF: u32 = 94; /// 16 bits GP-rel. address. pub const R_PARISC_GPREL16DF: u32 = 95; /// 64 bits LT-rel. address. pub const R_PARISC_LTOFF64: u32 = 96; /// LT-rel. address, right 14 bits. pub const R_PARISC_LTOFF14WR: u32 = 99; /// LT-rel. address, right 14 bits. pub const R_PARISC_LTOFF14DR: u32 = 100; /// 16 bits LT-rel. address. pub const R_PARISC_LTOFF16F: u32 = 101; /// 16 bits LT-rel. address. pub const R_PARISC_LTOFF16WF: u32 = 102; /// 16 bits LT-rel. address. pub const R_PARISC_LTOFF16DF: u32 = 103; /// 64 bits section rel. address. pub const R_PARISC_SECREL64: u32 = 104; /// 64 bits segment rel. address. pub const R_PARISC_SEGREL64: u32 = 112; /// PLT-rel. address, right 14 bits. pub const R_PARISC_PLTOFF14WR: u32 = 115; /// PLT-rel. address, right 14 bits. pub const R_PARISC_PLTOFF14DR: u32 = 116; /// 16 bits LT-rel. address. pub const R_PARISC_PLTOFF16F: u32 = 117; /// 16 bits PLT-rel. address. pub const R_PARISC_PLTOFF16WF: u32 = 118; /// 16 bits PLT-rel. address. pub const R_PARISC_PLTOFF16DF: u32 = 119; /// 64 bits LT-rel. function ptr. pub const R_PARISC_LTOFF_FPTR64: u32 = 120; /// LT-rel. fct. ptr., right 14 bits. pub const R_PARISC_LTOFF_FPTR14WR: u32 = 123; /// LT-rel. fct. ptr., right 14 bits. pub const R_PARISC_LTOFF_FPTR14DR: u32 = 124; /// 16 bits LT-rel. function ptr. pub const R_PARISC_LTOFF_FPTR16F: u32 = 125; /// 16 bits LT-rel. function ptr. pub const R_PARISC_LTOFF_FPTR16WF: u32 = 126; /// 16 bits LT-rel. function ptr. pub const R_PARISC_LTOFF_FPTR16DF: u32 = 127; pub const R_PARISC_LORESERVE: u32 = 128; /// Copy relocation. pub const R_PARISC_COPY: u32 = 128; /// Dynamic reloc, imported PLT pub const R_PARISC_IPLT: u32 = 129; /// Dynamic reloc, exported PLT pub const R_PARISC_EPLT: u32 = 130; /// 32 bits TP-rel. address. pub const R_PARISC_TPREL32: u32 = 153; /// TP-rel. address, left 21 bits. pub const R_PARISC_TPREL21L: u32 = 154; /// TP-rel. address, right 14 bits. pub const R_PARISC_TPREL14R: u32 = 158; /// LT-TP-rel. address, left 21 bits. pub const R_PARISC_LTOFF_TP21L: u32 = 162; /// LT-TP-rel. address, right 14 bits. pub const R_PARISC_LTOFF_TP14R: u32 = 166; /// 14 bits LT-TP-rel. address. pub const R_PARISC_LTOFF_TP14F: u32 = 167; /// 64 bits TP-rel. address. pub const R_PARISC_TPREL64: u32 = 216; /// TP-rel. address, right 14 bits. pub const R_PARISC_TPREL14WR: u32 = 219; /// TP-rel. address, right 14 bits. pub const R_PARISC_TPREL14DR: u32 = 220; /// 16 bits TP-rel. address. pub const R_PARISC_TPREL16F: u32 = 221; /// 16 bits TP-rel. address. pub const R_PARISC_TPREL16WF: u32 = 222; /// 16 bits TP-rel. address. pub const R_PARISC_TPREL16DF: u32 = 223; /// 64 bits LT-TP-rel. address. pub const R_PARISC_LTOFF_TP64: u32 = 224; /// LT-TP-rel. address, right 14 bits. pub const R_PARISC_LTOFF_TP14WR: u32 = 227; /// LT-TP-rel. address, right 14 bits. pub const R_PARISC_LTOFF_TP14DR: u32 = 228; /// 16 bits LT-TP-rel. address. pub const R_PARISC_LTOFF_TP16F: u32 = 229; /// 16 bits LT-TP-rel. address. pub const R_PARISC_LTOFF_TP16WF: u32 = 230; /// 16 bits LT-TP-rel. address. pub const R_PARISC_LTOFF_TP16DF: u32 = 231; pub const R_PARISC_GNU_VTENTRY: u32 = 232; pub const R_PARISC_GNU_VTINHERIT: u32 = 233; /// GD 21-bit left. pub const R_PARISC_TLS_GD21L: u32 = 234; /// GD 14-bit right. pub const R_PARISC_TLS_GD14R: u32 = 235; /// GD call to __t_g_a. pub const R_PARISC_TLS_GDCALL: u32 = 236; /// LD module 21-bit left. pub const R_PARISC_TLS_LDM21L: u32 = 237; /// LD module 14-bit right. pub const R_PARISC_TLS_LDM14R: u32 = 238; /// LD module call to __t_g_a. pub const R_PARISC_TLS_LDMCALL: u32 = 239; /// LD offset 21-bit left. pub const R_PARISC_TLS_LDO21L: u32 = 240; /// LD offset 14-bit right. pub const R_PARISC_TLS_LDO14R: u32 = 241; /// DTP module 32-bit. pub const R_PARISC_TLS_DTPMOD32: u32 = 242; /// DTP module 64-bit. pub const R_PARISC_TLS_DTPMOD64: u32 = 243; /// DTP offset 32-bit. pub const R_PARISC_TLS_DTPOFF32: u32 = 244; /// DTP offset 32-bit. pub const R_PARISC_TLS_DTPOFF64: u32 = 245; pub const R_PARISC_TLS_LE21L: u32 = R_PARISC_TPREL21L; pub const R_PARISC_TLS_LE14R: u32 = R_PARISC_TPREL14R; pub const R_PARISC_TLS_IE21L: u32 = R_PARISC_LTOFF_TP21L; pub const R_PARISC_TLS_IE14R: u32 = R_PARISC_LTOFF_TP14R; pub const R_PARISC_TLS_TPREL32: u32 = R_PARISC_TPREL32; pub const R_PARISC_TLS_TPREL64: u32 = R_PARISC_TPREL64; pub const R_PARISC_HIRESERVE: u32 = 255; // PA-RISC values for `ProgramHeader*::p_type`. pub const PT_HP_TLS: u32 = PT_LOOS + 0x0; pub const PT_HP_CORE_NONE: u32 = PT_LOOS + 0x1; pub const PT_HP_CORE_VERSION: u32 = PT_LOOS + 0x2; pub const PT_HP_CORE_KERNEL: u32 = PT_LOOS + 0x3; pub const PT_HP_CORE_COMM: u32 = PT_LOOS + 0x4; pub const PT_HP_CORE_PROC: u32 = PT_LOOS + 0x5; pub const PT_HP_CORE_LOADABLE: u32 = PT_LOOS + 0x6; pub const PT_HP_CORE_STACK: u32 = PT_LOOS + 0x7; pub const PT_HP_CORE_SHM: u32 = PT_LOOS + 0x8; pub const PT_HP_CORE_MMF: u32 = PT_LOOS + 0x9; pub const PT_HP_PARALLEL: u32 = PT_LOOS + 0x10; pub const PT_HP_FASTBIND: u32 = PT_LOOS + 0x11; pub const PT_HP_OPT_ANNOT: u32 = PT_LOOS + 0x12; pub const PT_HP_HSL_ANNOT: u32 = PT_LOOS + 0x13; pub const PT_HP_STACK: u32 = PT_LOOS + 0x14; pub const PT_PARISC_ARCHEXT: u32 = 0x7000_0000; pub const PT_PARISC_UNWIND: u32 = 0x7000_0001; // PA-RISC values for `ProgramHeader*::p_flags`. pub const PF_PARISC_SBP: u32 = 0x0800_0000; pub const PF_HP_PAGE_SIZE: u32 = 0x0010_0000; pub const PF_HP_FAR_SHARED: u32 = 0x0020_0000; pub const PF_HP_NEAR_SHARED: u32 = 0x0040_0000; pub const PF_HP_CODE: u32 = 0x0100_0000; pub const PF_HP_MODIFY: u32 = 0x0200_0000; pub const PF_HP_LAZYSWAP: u32 = 0x0400_0000; pub const PF_HP_SBP: u32 = 0x0800_0000; // Alpha specific definitions. // Alpha values for `FileHeader64::e_flags`. /// All addresses must be < 2GB. pub const EF_ALPHA_32BIT: u32 = 1; /// Relocations for relaxing exist. pub const EF_ALPHA_CANRELAX: u32 = 2; // Alpha values for `SectionHeader64::sh_type`. // These two are primerily concerned with ECOFF debugging info. pub const SHT_ALPHA_DEBUG: u32 = 0x7000_0001; pub const SHT_ALPHA_REGINFO: u32 = 0x7000_0002; // Alpha values for `SectionHeader64::sh_flags`. pub const SHF_ALPHA_GPREL: u32 = 0x1000_0000; // Alpha values for `Sym64::st_other`. /// No PV required. pub const STO_ALPHA_NOPV: u8 = 0x80; /// PV only used for initial ldgp. pub const STO_ALPHA_STD_GPLOAD: u8 = 0x88; // Alpha values for `Rel64::r_type`. /// No reloc pub const R_ALPHA_NONE: u32 = 0; /// Direct 32 bit pub const R_ALPHA_REFLONG: u32 = 1; /// Direct 64 bit pub const R_ALPHA_REFQUAD: u32 = 2; /// GP relative 32 bit pub const R_ALPHA_GPREL32: u32 = 3; /// GP relative 16 bit w/optimization pub const R_ALPHA_LITERAL: u32 = 4; /// Optimization hint for LITERAL pub const R_ALPHA_LITUSE: u32 = 5; /// Add displacement to GP pub const R_ALPHA_GPDISP: u32 = 6; /// PC+4 relative 23 bit shifted pub const R_ALPHA_BRADDR: u32 = 7; /// PC+4 relative 16 bit shifted pub const R_ALPHA_HINT: u32 = 8; /// PC relative 16 bit pub const R_ALPHA_SREL16: u32 = 9; /// PC relative 32 bit pub const R_ALPHA_SREL32: u32 = 10; /// PC relative 64 bit pub const R_ALPHA_SREL64: u32 = 11; /// GP relative 32 bit, high 16 bits pub const R_ALPHA_GPRELHIGH: u32 = 17; /// GP relative 32 bit, low 16 bits pub const R_ALPHA_GPRELLOW: u32 = 18; /// GP relative 16 bit pub const R_ALPHA_GPREL16: u32 = 19; /// Copy symbol at runtime pub const R_ALPHA_COPY: u32 = 24; /// Create GOT entry pub const R_ALPHA_GLOB_DAT: u32 = 25; /// Create PLT entry pub const R_ALPHA_JMP_SLOT: u32 = 26; /// Adjust by program base pub const R_ALPHA_RELATIVE: u32 = 27; pub const R_ALPHA_TLS_GD_HI: u32 = 28; pub const R_ALPHA_TLSGD: u32 = 29; pub const R_ALPHA_TLS_LDM: u32 = 30; pub const R_ALPHA_DTPMOD64: u32 = 31; pub const R_ALPHA_GOTDTPREL: u32 = 32; pub const R_ALPHA_DTPREL64: u32 = 33; pub const R_ALPHA_DTPRELHI: u32 = 34; pub const R_ALPHA_DTPRELLO: u32 = 35; pub const R_ALPHA_DTPREL16: u32 = 36; pub const R_ALPHA_GOTTPREL: u32 = 37; pub const R_ALPHA_TPREL64: u32 = 38; pub const R_ALPHA_TPRELHI: u32 = 39; pub const R_ALPHA_TPRELLO: u32 = 40; pub const R_ALPHA_TPREL16: u32 = 41; // Magic values of the `R_ALPHA_LITUSE` relocation addend. pub const LITUSE_ALPHA_ADDR: u32 = 0; pub const LITUSE_ALPHA_BASE: u32 = 1; pub const LITUSE_ALPHA_BYTOFF: u32 = 2; pub const LITUSE_ALPHA_JSR: u32 = 3; pub const LITUSE_ALPHA_TLS_GD: u32 = 4; pub const LITUSE_ALPHA_TLS_LDM: u32 = 5; // Alpha values for `Dyn64::d_tag`. pub const DT_ALPHA_PLTRO: u32 = DT_LOPROC + 0; // PowerPC specific declarations. // PowerPC values for `FileHeader*::e_flags`. /// PowerPC embedded flag pub const EF_PPC_EMB: u32 = 0x8000_0000; // Cygnus local bits below . /// PowerPC -mrelocatable flag pub const EF_PPC_RELOCATABLE: u32 = 0x0001_0000; /// PowerPC -mrelocatable-lib flag pub const EF_PPC_RELOCATABLE_LIB: u32 = 0x0000_8000; // PowerPC values for `Rel*::r_type` defined by the ABIs. pub const R_PPC_NONE: u32 = 0; /// 32bit absolute address pub const R_PPC_ADDR32: u32 = 1; /// 26bit address, 2 bits ignored. pub const R_PPC_ADDR24: u32 = 2; /// 16bit absolute address pub const R_PPC_ADDR16: u32 = 3; /// lower 16bit of absolute address pub const R_PPC_ADDR16_LO: u32 = 4; /// high 16bit of absolute address pub const R_PPC_ADDR16_HI: u32 = 5; /// adjusted high 16bit pub const R_PPC_ADDR16_HA: u32 = 6; /// 16bit address, 2 bits ignored pub const R_PPC_ADDR14: u32 = 7; pub const R_PPC_ADDR14_BRTAKEN: u32 = 8; pub const R_PPC_ADDR14_BRNTAKEN: u32 = 9; /// PC relative 26 bit pub const R_PPC_REL24: u32 = 10; /// PC relative 16 bit pub const R_PPC_REL14: u32 = 11; pub const R_PPC_REL14_BRTAKEN: u32 = 12; pub const R_PPC_REL14_BRNTAKEN: u32 = 13; pub const R_PPC_GOT16: u32 = 14; pub const R_PPC_GOT16_LO: u32 = 15; pub const R_PPC_GOT16_HI: u32 = 16; pub const R_PPC_GOT16_HA: u32 = 17; pub const R_PPC_PLTREL24: u32 = 18; pub const R_PPC_COPY: u32 = 19; pub const R_PPC_GLOB_DAT: u32 = 20; pub const R_PPC_JMP_SLOT: u32 = 21; pub const R_PPC_RELATIVE: u32 = 22; pub const R_PPC_LOCAL24PC: u32 = 23; pub const R_PPC_UADDR32: u32 = 24; pub const R_PPC_UADDR16: u32 = 25; pub const R_PPC_REL32: u32 = 26; pub const R_PPC_PLT32: u32 = 27; pub const R_PPC_PLTREL32: u32 = 28; pub const R_PPC_PLT16_LO: u32 = 29; pub const R_PPC_PLT16_HI: u32 = 30; pub const R_PPC_PLT16_HA: u32 = 31; pub const R_PPC_SDAREL16: u32 = 32; pub const R_PPC_SECTOFF: u32 = 33; pub const R_PPC_SECTOFF_LO: u32 = 34; pub const R_PPC_SECTOFF_HI: u32 = 35; pub const R_PPC_SECTOFF_HA: u32 = 36; // PowerPC values for `Rel*::r_type` defined for the TLS access ABI. /// none (sym+add)@tls pub const R_PPC_TLS: u32 = 67; /// word32 (sym+add)@dtpmod pub const R_PPC_DTPMOD32: u32 = 68; /// half16* (sym+add)@tprel pub const R_PPC_TPREL16: u32 = 69; /// half16 (sym+add)@tprel@l pub const R_PPC_TPREL16_LO: u32 = 70; /// half16 (sym+add)@tprel@h pub const R_PPC_TPREL16_HI: u32 = 71; /// half16 (sym+add)@tprel@ha pub const R_PPC_TPREL16_HA: u32 = 72; /// word32 (sym+add)@tprel pub const R_PPC_TPREL32: u32 = 73; /// half16*(sym+add)@dtprel pub const R_PPC_DTPREL16: u32 = 74; /// half16 (sym+add)@dtprel@l pub const R_PPC_DTPREL16_LO: u32 = 75; /// half16 (sym+add)@dtprel@h pub const R_PPC_DTPREL16_HI: u32 = 76; /// half16 (sym+add)@dtprel@ha pub const R_PPC_DTPREL16_HA: u32 = 77; /// word32 (sym+add)@dtprel pub const R_PPC_DTPREL32: u32 = 78; /// half16* (sym+add)@got@tlsgd pub const R_PPC_GOT_TLSGD16: u32 = 79; /// half16 (sym+add)@got@tlsgd@l pub const R_PPC_GOT_TLSGD16_LO: u32 = 80; /// half16 (sym+add)@got@tlsgd@h pub const R_PPC_GOT_TLSGD16_HI: u32 = 81; /// half16 (sym+add)@got@tlsgd@ha pub const R_PPC_GOT_TLSGD16_HA: u32 = 82; /// half16* (sym+add)@got@tlsld pub const R_PPC_GOT_TLSLD16: u32 = 83; /// half16 (sym+add)@got@tlsld@l pub const R_PPC_GOT_TLSLD16_LO: u32 = 84; /// half16 (sym+add)@got@tlsld@h pub const R_PPC_GOT_TLSLD16_HI: u32 = 85; /// half16 (sym+add)@got@tlsld@ha pub const R_PPC_GOT_TLSLD16_HA: u32 = 86; /// half16* (sym+add)@got@tprel pub const R_PPC_GOT_TPREL16: u32 = 87; /// half16 (sym+add)@got@tprel@l pub const R_PPC_GOT_TPREL16_LO: u32 = 88; /// half16 (sym+add)@got@tprel@h pub const R_PPC_GOT_TPREL16_HI: u32 = 89; /// half16 (sym+add)@got@tprel@ha pub const R_PPC_GOT_TPREL16_HA: u32 = 90; /// half16* (sym+add)@got@dtprel pub const R_PPC_GOT_DTPREL16: u32 = 91; /// half16* (sym+add)@got@dtprel@l pub const R_PPC_GOT_DTPREL16_LO: u32 = 92; /// half16* (sym+add)@got@dtprel@h pub const R_PPC_GOT_DTPREL16_HI: u32 = 93; /// half16* (sym+add)@got@dtprel@ha pub const R_PPC_GOT_DTPREL16_HA: u32 = 94; /// none (sym+add)@tlsgd pub const R_PPC_TLSGD: u32 = 95; /// none (sym+add)@tlsld pub const R_PPC_TLSLD: u32 = 96; // PowerPC values for `Rel*::r_type` from the Embedded ELF ABI. pub const R_PPC_EMB_NADDR32: u32 = 101; pub const R_PPC_EMB_NADDR16: u32 = 102; pub const R_PPC_EMB_NADDR16_LO: u32 = 103; pub const R_PPC_EMB_NADDR16_HI: u32 = 104; pub const R_PPC_EMB_NADDR16_HA: u32 = 105; pub const R_PPC_EMB_SDAI16: u32 = 106; pub const R_PPC_EMB_SDA2I16: u32 = 107; pub const R_PPC_EMB_SDA2REL: u32 = 108; /// 16 bit offset in SDA pub const R_PPC_EMB_SDA21: u32 = 109; pub const R_PPC_EMB_MRKREF: u32 = 110; pub const R_PPC_EMB_RELSEC16: u32 = 111; pub const R_PPC_EMB_RELST_LO: u32 = 112; pub const R_PPC_EMB_RELST_HI: u32 = 113; pub const R_PPC_EMB_RELST_HA: u32 = 114; pub const R_PPC_EMB_BIT_FLD: u32 = 115; /// 16 bit relative offset in SDA pub const R_PPC_EMB_RELSDA: u32 = 116; // Diab tool values for `Rel*::r_type`. /// like EMB_SDA21, but lower 16 bit pub const R_PPC_DIAB_SDA21_LO: u32 = 180; /// like EMB_SDA21, but high 16 bit pub const R_PPC_DIAB_SDA21_HI: u32 = 181; /// like EMB_SDA21, adjusted high 16 pub const R_PPC_DIAB_SDA21_HA: u32 = 182; /// like EMB_RELSDA, but lower 16 bit pub const R_PPC_DIAB_RELSDA_LO: u32 = 183; /// like EMB_RELSDA, but high 16 bit pub const R_PPC_DIAB_RELSDA_HI: u32 = 184; /// like EMB_RELSDA, adjusted high 16 pub const R_PPC_DIAB_RELSDA_HA: u32 = 185; /// GNU extension to support local ifunc. pub const R_PPC_IRELATIVE: u32 = 248; // GNU relocs used in PIC code sequences. /// half16 (sym+add-.) pub const R_PPC_REL16: u32 = 249; /// half16 (sym+add-.)@l pub const R_PPC_REL16_LO: u32 = 250; /// half16 (sym+add-.)@h pub const R_PPC_REL16_HI: u32 = 251; /// half16 (sym+add-.)@ha pub const R_PPC_REL16_HA: u32 = 252; /// This is a phony reloc to handle any old fashioned TOC16 references that may /// still be in object files. pub const R_PPC_TOC16: u32 = 255; // PowerPC specific values for `Dyn*::d_tag`. pub const DT_PPC_GOT: u32 = DT_LOPROC + 0; pub const DT_PPC_OPT: u32 = DT_LOPROC + 1; // PowerPC specific values for the `DT_PPC_OPT` entry. pub const PPC_OPT_TLS: u32 = 1; // PowerPC64 values for `Rel*::r_type` defined by the ABIs. pub const R_PPC64_NONE: u32 = R_PPC_NONE; /// 32bit absolute address pub const R_PPC64_ADDR32: u32 = R_PPC_ADDR32; /// 26bit address, word aligned pub const R_PPC64_ADDR24: u32 = R_PPC_ADDR24; /// 16bit absolute address pub const R_PPC64_ADDR16: u32 = R_PPC_ADDR16; /// lower 16bits of address pub const R_PPC64_ADDR16_LO: u32 = R_PPC_ADDR16_LO; /// high 16bits of address. pub const R_PPC64_ADDR16_HI: u32 = R_PPC_ADDR16_HI; /// adjusted high 16bits. pub const R_PPC64_ADDR16_HA: u32 = R_PPC_ADDR16_HA; /// 16bit address, word aligned pub const R_PPC64_ADDR14: u32 = R_PPC_ADDR14; pub const R_PPC64_ADDR14_BRTAKEN: u32 = R_PPC_ADDR14_BRTAKEN; pub const R_PPC64_ADDR14_BRNTAKEN: u32 = R_PPC_ADDR14_BRNTAKEN; /// PC-rel. 26 bit, word aligned pub const R_PPC64_REL24: u32 = R_PPC_REL24; /// PC relative 16 bit pub const R_PPC64_REL14: u32 = R_PPC_REL14; pub const R_PPC64_REL14_BRTAKEN: u32 = R_PPC_REL14_BRTAKEN; pub const R_PPC64_REL14_BRNTAKEN: u32 = R_PPC_REL14_BRNTAKEN; pub const R_PPC64_GOT16: u32 = R_PPC_GOT16; pub const R_PPC64_GOT16_LO: u32 = R_PPC_GOT16_LO; pub const R_PPC64_GOT16_HI: u32 = R_PPC_GOT16_HI; pub const R_PPC64_GOT16_HA: u32 = R_PPC_GOT16_HA; pub const R_PPC64_COPY: u32 = R_PPC_COPY; pub const R_PPC64_GLOB_DAT: u32 = R_PPC_GLOB_DAT; pub const R_PPC64_JMP_SLOT: u32 = R_PPC_JMP_SLOT; pub const R_PPC64_RELATIVE: u32 = R_PPC_RELATIVE; pub const R_PPC64_UADDR32: u32 = R_PPC_UADDR32; pub const R_PPC64_UADDR16: u32 = R_PPC_UADDR16; pub const R_PPC64_REL32: u32 = R_PPC_REL32; pub const R_PPC64_PLT32: u32 = R_PPC_PLT32; pub const R_PPC64_PLTREL32: u32 = R_PPC_PLTREL32; pub const R_PPC64_PLT16_LO: u32 = R_PPC_PLT16_LO; pub const R_PPC64_PLT16_HI: u32 = R_PPC_PLT16_HI; pub const R_PPC64_PLT16_HA: u32 = R_PPC_PLT16_HA; pub const R_PPC64_SECTOFF: u32 = R_PPC_SECTOFF; pub const R_PPC64_SECTOFF_LO: u32 = R_PPC_SECTOFF_LO; pub const R_PPC64_SECTOFF_HI: u32 = R_PPC_SECTOFF_HI; pub const R_PPC64_SECTOFF_HA: u32 = R_PPC_SECTOFF_HA; /// word30 (S + A - P) >> 2 pub const R_PPC64_ADDR30: u32 = 37; /// doubleword64 S + A pub const R_PPC64_ADDR64: u32 = 38; /// half16 #higher(S + A) pub const R_PPC64_ADDR16_HIGHER: u32 = 39; /// half16 #highera(S + A) pub const R_PPC64_ADDR16_HIGHERA: u32 = 40; /// half16 #highest(S + A) pub const R_PPC64_ADDR16_HIGHEST: u32 = 41; /// half16 #highesta(S + A) pub const R_PPC64_ADDR16_HIGHESTA: u32 = 42; /// doubleword64 S + A pub const R_PPC64_UADDR64: u32 = 43; /// doubleword64 S + A - P pub const R_PPC64_REL64: u32 = 44; /// doubleword64 L + A pub const R_PPC64_PLT64: u32 = 45; /// doubleword64 L + A - P pub const R_PPC64_PLTREL64: u32 = 46; /// half16* S + A - .TOC pub const R_PPC64_TOC16: u32 = 47; /// half16 #lo(S + A - .TOC.) pub const R_PPC64_TOC16_LO: u32 = 48; /// half16 #hi(S + A - .TOC.) pub const R_PPC64_TOC16_HI: u32 = 49; /// half16 #ha(S + A - .TOC.) pub const R_PPC64_TOC16_HA: u32 = 50; /// doubleword64 .TOC pub const R_PPC64_TOC: u32 = 51; /// half16* M + A pub const R_PPC64_PLTGOT16: u32 = 52; /// half16 #lo(M + A) pub const R_PPC64_PLTGOT16_LO: u32 = 53; /// half16 #hi(M + A) pub const R_PPC64_PLTGOT16_HI: u32 = 54; /// half16 #ha(M + A) pub const R_PPC64_PLTGOT16_HA: u32 = 55; /// half16ds* (S + A) >> 2 pub const R_PPC64_ADDR16_DS: u32 = 56; /// half16ds #lo(S + A) >> 2 pub const R_PPC64_ADDR16_LO_DS: u32 = 57; /// half16ds* (G + A) >> 2 pub const R_PPC64_GOT16_DS: u32 = 58; /// half16ds #lo(G + A) >> 2 pub const R_PPC64_GOT16_LO_DS: u32 = 59; /// half16ds #lo(L + A) >> 2 pub const R_PPC64_PLT16_LO_DS: u32 = 60; /// half16ds* (R + A) >> 2 pub const R_PPC64_SECTOFF_DS: u32 = 61; /// half16ds #lo(R + A) >> 2 pub const R_PPC64_SECTOFF_LO_DS: u32 = 62; /// half16ds* (S + A - .TOC.) >> 2 pub const R_PPC64_TOC16_DS: u32 = 63; /// half16ds #lo(S + A - .TOC.) >> 2 pub const R_PPC64_TOC16_LO_DS: u32 = 64; /// half16ds* (M + A) >> 2 pub const R_PPC64_PLTGOT16_DS: u32 = 65; /// half16ds #lo(M + A) >> 2 pub const R_PPC64_PLTGOT16_LO_DS: u32 = 66; // PowerPC64 values for `Rel*::r_type` defined for the TLS access ABI. /// none (sym+add)@tls pub const R_PPC64_TLS: u32 = 67; /// doubleword64 (sym+add)@dtpmod pub const R_PPC64_DTPMOD64: u32 = 68; /// half16* (sym+add)@tprel pub const R_PPC64_TPREL16: u32 = 69; /// half16 (sym+add)@tprel@l pub const R_PPC64_TPREL16_LO: u32 = 70; /// half16 (sym+add)@tprel@h pub const R_PPC64_TPREL16_HI: u32 = 71; /// half16 (sym+add)@tprel@ha pub const R_PPC64_TPREL16_HA: u32 = 72; /// doubleword64 (sym+add)@tprel pub const R_PPC64_TPREL64: u32 = 73; /// half16* (sym+add)@dtprel pub const R_PPC64_DTPREL16: u32 = 74; /// half16 (sym+add)@dtprel@l pub const R_PPC64_DTPREL16_LO: u32 = 75; /// half16 (sym+add)@dtprel@h pub const R_PPC64_DTPREL16_HI: u32 = 76; /// half16 (sym+add)@dtprel@ha pub const R_PPC64_DTPREL16_HA: u32 = 77; /// doubleword64 (sym+add)@dtprel pub const R_PPC64_DTPREL64: u32 = 78; /// half16* (sym+add)@got@tlsgd pub const R_PPC64_GOT_TLSGD16: u32 = 79; /// half16 (sym+add)@got@tlsgd@l pub const R_PPC64_GOT_TLSGD16_LO: u32 = 80; /// half16 (sym+add)@got@tlsgd@h pub const R_PPC64_GOT_TLSGD16_HI: u32 = 81; /// half16 (sym+add)@got@tlsgd@ha pub const R_PPC64_GOT_TLSGD16_HA: u32 = 82; /// half16* (sym+add)@got@tlsld pub const R_PPC64_GOT_TLSLD16: u32 = 83; /// half16 (sym+add)@got@tlsld@l pub const R_PPC64_GOT_TLSLD16_LO: u32 = 84; /// half16 (sym+add)@got@tlsld@h pub const R_PPC64_GOT_TLSLD16_HI: u32 = 85; /// half16 (sym+add)@got@tlsld@ha pub const R_PPC64_GOT_TLSLD16_HA: u32 = 86; /// half16ds* (sym+add)@got@tprel pub const R_PPC64_GOT_TPREL16_DS: u32 = 87; /// half16ds (sym+add)@got@tprel@l pub const R_PPC64_GOT_TPREL16_LO_DS: u32 = 88; /// half16 (sym+add)@got@tprel@h pub const R_PPC64_GOT_TPREL16_HI: u32 = 89; /// half16 (sym+add)@got@tprel@ha pub const R_PPC64_GOT_TPREL16_HA: u32 = 90; /// half16ds* (sym+add)@got@dtprel pub const R_PPC64_GOT_DTPREL16_DS: u32 = 91; /// half16ds (sym+add)@got@dtprel@l pub const R_PPC64_GOT_DTPREL16_LO_DS: u32 = 92; /// half16 (sym+add)@got@dtprel@h pub const R_PPC64_GOT_DTPREL16_HI: u32 = 93; /// half16 (sym+add)@got@dtprel@ha pub const R_PPC64_GOT_DTPREL16_HA: u32 = 94; /// half16ds* (sym+add)@tprel pub const R_PPC64_TPREL16_DS: u32 = 95; /// half16ds (sym+add)@tprel@l pub const R_PPC64_TPREL16_LO_DS: u32 = 96; /// half16 (sym+add)@tprel@higher pub const R_PPC64_TPREL16_HIGHER: u32 = 97; /// half16 (sym+add)@tprel@highera pub const R_PPC64_TPREL16_HIGHERA: u32 = 98; /// half16 (sym+add)@tprel@highest pub const R_PPC64_TPREL16_HIGHEST: u32 = 99; /// half16 (sym+add)@tprel@highesta pub const R_PPC64_TPREL16_HIGHESTA: u32 = 100; /// half16ds* (sym+add)@dtprel pub const R_PPC64_DTPREL16_DS: u32 = 101; /// half16ds (sym+add)@dtprel@l pub const R_PPC64_DTPREL16_LO_DS: u32 = 102; /// half16 (sym+add)@dtprel@higher pub const R_PPC64_DTPREL16_HIGHER: u32 = 103; /// half16 (sym+add)@dtprel@highera pub const R_PPC64_DTPREL16_HIGHERA: u32 = 104; /// half16 (sym+add)@dtprel@highest pub const R_PPC64_DTPREL16_HIGHEST: u32 = 105; /// half16 (sym+add)@dtprel@highesta pub const R_PPC64_DTPREL16_HIGHESTA: u32 = 106; /// none (sym+add)@tlsgd pub const R_PPC64_TLSGD: u32 = 107; /// none (sym+add)@tlsld pub const R_PPC64_TLSLD: u32 = 108; /// none pub const R_PPC64_TOCSAVE: u32 = 109; // Added when HA and HI relocs were changed to report overflows. pub const R_PPC64_ADDR16_HIGH: u32 = 110; pub const R_PPC64_ADDR16_HIGHA: u32 = 111; pub const R_PPC64_TPREL16_HIGH: u32 = 112; pub const R_PPC64_TPREL16_HIGHA: u32 = 113; pub const R_PPC64_DTPREL16_HIGH: u32 = 114; pub const R_PPC64_DTPREL16_HIGHA: u32 = 115; /// GNU extension to support local ifunc. pub const R_PPC64_JMP_IREL: u32 = 247; /// GNU extension to support local ifunc. pub const R_PPC64_IRELATIVE: u32 = 248; /// half16 (sym+add-.) pub const R_PPC64_REL16: u32 = 249; /// half16 (sym+add-.)@l pub const R_PPC64_REL16_LO: u32 = 250; /// half16 (sym+add-.)@h pub const R_PPC64_REL16_HI: u32 = 251; /// half16 (sym+add-.)@ha pub const R_PPC64_REL16_HA: u32 = 252; // PowerPC64 values for `FileHeader64::e_flags. /// PowerPC64 bits specifying ABI. /// /// 1 for original function descriptor using ABI, /// 2 for revised ABI without function descriptors, /// 0 for unspecified or not using any features affected by the differences. pub const EF_PPC64_ABI: u32 = 3; // PowerPC64 values for `Dyn64::d_tag. pub const DT_PPC64_GLINK: u32 = DT_LOPROC + 0; pub const DT_PPC64_OPD: u32 = DT_LOPROC + 1; pub const DT_PPC64_OPDSZ: u32 = DT_LOPROC + 2; pub const DT_PPC64_OPT: u32 = DT_LOPROC + 3; // PowerPC64 bits for `DT_PPC64_OPT` entry. pub const PPC64_OPT_TLS: u32 = 1; pub const PPC64_OPT_MULTI_TOC: u32 = 2; pub const PPC64_OPT_LOCALENTRY: u32 = 4; // PowerPC64 values for `Sym64::st_other. pub const STO_PPC64_LOCAL_BIT: u8 = 5; pub const STO_PPC64_LOCAL_MASK: u8 = 7 << STO_PPC64_LOCAL_BIT; // ARM specific declarations. // ARM values for `FileHeader*::e_flags`. pub const EF_ARM_RELEXEC: u32 = 0x01; pub const EF_ARM_HASENTRY: u32 = 0x02; pub const EF_ARM_INTERWORK: u32 = 0x04; pub const EF_ARM_APCS_26: u32 = 0x08; pub const EF_ARM_APCS_FLOAT: u32 = 0x10; pub const EF_ARM_PIC: u32 = 0x20; /// 8-bit structure alignment is in use pub const EF_ARM_ALIGN8: u32 = 0x40; pub const EF_ARM_NEW_ABI: u32 = 0x80; pub const EF_ARM_OLD_ABI: u32 = 0x100; pub const EF_ARM_SOFT_FLOAT: u32 = 0x200; pub const EF_ARM_VFP_FLOAT: u32 = 0x400; pub const EF_ARM_MAVERICK_FLOAT: u32 = 0x800; /// NB conflicts with EF_ARM_SOFT_FLOAT pub const EF_ARM_ABI_FLOAT_SOFT: u32 = 0x200; /// NB conflicts with EF_ARM_VFP_FLOAT pub const EF_ARM_ABI_FLOAT_HARD: u32 = 0x400; // Other constants defined in the ARM ELF spec. version B-01. // NB. These conflict with values defined above. pub const EF_ARM_SYMSARESORTED: u32 = 0x04; pub const EF_ARM_DYNSYMSUSESEGIDX: u32 = 0x08; pub const EF_ARM_MAPSYMSFIRST: u32 = 0x10; // Constants defined in AAELF. pub const EF_ARM_BE8: u32 = 0x0080_0000; pub const EF_ARM_LE8: u32 = 0x0040_0000; pub const EF_ARM_EABIMASK: u32 = 0xff00_0000; pub const EF_ARM_EABI_UNKNOWN: u32 = 0x0000_0000; pub const EF_ARM_EABI_VER1: u32 = 0x0100_0000; pub const EF_ARM_EABI_VER2: u32 = 0x0200_0000; pub const EF_ARM_EABI_VER3: u32 = 0x0300_0000; pub const EF_ARM_EABI_VER4: u32 = 0x0400_0000; pub const EF_ARM_EABI_VER5: u32 = 0x0500_0000; // ARM Thumb values for `st_type` component of `Sym*::st_info`. /// A Thumb function. pub const STT_ARM_TFUNC: u8 = STT_LOPROC; /// A Thumb label. pub const STT_ARM_16BIT: u8 = STT_HIPROC; // ARM values for `SectionHeader*::sh_flags`. /// Section contains an entry point pub const SHF_ARM_ENTRYSECT: u32 = 0x1000_0000; /// Section may be multiply defined in the input to a link step. pub const SHF_ARM_COMDEF: u32 = 0x8000_0000; // ARM values for `ProgramHeader*::p_flags`. /// Segment contains the location addressed by the static base. pub const PF_ARM_SB: u32 = 0x1000_0000; /// Position-independent segment. pub const PF_ARM_PI: u32 = 0x2000_0000; /// Absolute segment. pub const PF_ARM_ABS: u32 = 0x4000_0000; // ARM values for `ProgramHeader*::p_type`. /// ARM unwind segment. pub const PT_ARM_EXIDX: u32 = PT_LOPROC + 1; // ARM values for `SectionHeader*::sh_type`. /// ARM unwind section. pub const SHT_ARM_EXIDX: u32 = SHT_LOPROC + 1; /// Preemption details. pub const SHT_ARM_PREEMPTMAP: u32 = SHT_LOPROC + 2; /// ARM attributes section. pub const SHT_ARM_ATTRIBUTES: u32 = SHT_LOPROC + 3; // AArch64 values for `SectionHeader*::sh_type`. /// AArch64 attributes section. pub const SHT_AARCH64_ATTRIBUTES: u32 = SHT_LOPROC + 3; // AArch64 values for `Rel*::r_type`. /// No relocation. pub const R_AARCH64_NONE: u32 = 0; // ILP32 AArch64 relocs. /// Direct 32 bit. pub const R_AARCH64_P32_ABS32: u32 = 1; /// Copy symbol at runtime. pub const R_AARCH64_P32_COPY: u32 = 180; /// Create GOT entry. pub const R_AARCH64_P32_GLOB_DAT: u32 = 181; /// Create PLT entry. pub const R_AARCH64_P32_JUMP_SLOT: u32 = 182; /// Adjust by program base. pub const R_AARCH64_P32_RELATIVE: u32 = 183; /// Module number, 32 bit. pub const R_AARCH64_P32_TLS_DTPMOD: u32 = 184; /// Module-relative offset, 32 bit. pub const R_AARCH64_P32_TLS_DTPREL: u32 = 185; /// TP-relative offset, 32 bit. pub const R_AARCH64_P32_TLS_TPREL: u32 = 186; /// TLS Descriptor. pub const R_AARCH64_P32_TLSDESC: u32 = 187; /// STT_GNU_IFUNC relocation. pub const R_AARCH64_P32_IRELATIVE: u32 = 188; // LP64 AArch64 relocs. /// Direct 64 bit. pub const R_AARCH64_ABS64: u32 = 257; /// Direct 32 bit. pub const R_AARCH64_ABS32: u32 = 258; /// Direct 16-bit. pub const R_AARCH64_ABS16: u32 = 259; /// PC-relative 64-bit. pub const R_AARCH64_PREL64: u32 = 260; /// PC-relative 32-bit. pub const R_AARCH64_PREL32: u32 = 261; /// PC-relative 16-bit. pub const R_AARCH64_PREL16: u32 = 262; /// Dir. MOVZ imm. from bits 15:0. pub const R_AARCH64_MOVW_UABS_G0: u32 = 263; /// Likewise for MOVK; no check. pub const R_AARCH64_MOVW_UABS_G0_NC: u32 = 264; /// Dir. MOVZ imm. from bits 31:16. pub const R_AARCH64_MOVW_UABS_G1: u32 = 265; /// Likewise for MOVK; no check. pub const R_AARCH64_MOVW_UABS_G1_NC: u32 = 266; /// Dir. MOVZ imm. from bits 47:32. pub const R_AARCH64_MOVW_UABS_G2: u32 = 267; /// Likewise for MOVK; no check. pub const R_AARCH64_MOVW_UABS_G2_NC: u32 = 268; /// Dir. MOV{K,Z} imm. from 63:48. pub const R_AARCH64_MOVW_UABS_G3: u32 = 269; /// Dir. MOV{N,Z} imm. from 15:0. pub const R_AARCH64_MOVW_SABS_G0: u32 = 270; /// Dir. MOV{N,Z} imm. from 31:16. pub const R_AARCH64_MOVW_SABS_G1: u32 = 271; /// Dir. MOV{N,Z} imm. from 47:32. pub const R_AARCH64_MOVW_SABS_G2: u32 = 272; /// PC-rel. LD imm. from bits 20:2. pub const R_AARCH64_LD_PREL_LO19: u32 = 273; /// PC-rel. ADR imm. from bits 20:0. pub const R_AARCH64_ADR_PREL_LO21: u32 = 274; /// Page-rel. ADRP imm. from 32:12. pub const R_AARCH64_ADR_PREL_PG_HI21: u32 = 275; /// Likewise; no overflow check. pub const R_AARCH64_ADR_PREL_PG_HI21_NC: u32 = 276; /// Dir. ADD imm. from bits 11:0. pub const R_AARCH64_ADD_ABS_LO12_NC: u32 = 277; /// Likewise for LD/ST; no check. pub const R_AARCH64_LDST8_ABS_LO12_NC: u32 = 278; /// PC-rel. TBZ/TBNZ imm. from 15:2. pub const R_AARCH64_TSTBR14: u32 = 279; /// PC-rel. cond. br. imm. from 20:2. pub const R_AARCH64_CONDBR19: u32 = 280; /// PC-rel. B imm. from bits 27:2. pub const R_AARCH64_JUMP26: u32 = 282; /// Likewise for CALL. pub const R_AARCH64_CALL26: u32 = 283; /// Dir. ADD imm. from bits 11:1. pub const R_AARCH64_LDST16_ABS_LO12_NC: u32 = 284; /// Likewise for bits 11:2. pub const R_AARCH64_LDST32_ABS_LO12_NC: u32 = 285; /// Likewise for bits 11:3. pub const R_AARCH64_LDST64_ABS_LO12_NC: u32 = 286; /// PC-rel. MOV{N,Z} imm. from 15:0. pub const R_AARCH64_MOVW_PREL_G0: u32 = 287; /// Likewise for MOVK; no check. pub const R_AARCH64_MOVW_PREL_G0_NC: u32 = 288; /// PC-rel. MOV{N,Z} imm. from 31:16. pub const R_AARCH64_MOVW_PREL_G1: u32 = 289; /// Likewise for MOVK; no check. pub const R_AARCH64_MOVW_PREL_G1_NC: u32 = 290; /// PC-rel. MOV{N,Z} imm. from 47:32. pub const R_AARCH64_MOVW_PREL_G2: u32 = 291; /// Likewise for MOVK; no check. pub const R_AARCH64_MOVW_PREL_G2_NC: u32 = 292; /// PC-rel. MOV{N,Z} imm. from 63:48. pub const R_AARCH64_MOVW_PREL_G3: u32 = 293; /// Dir. ADD imm. from bits 11:4. pub const R_AARCH64_LDST128_ABS_LO12_NC: u32 = 299; /// GOT-rel. off. MOV{N,Z} imm. 15:0. pub const R_AARCH64_MOVW_GOTOFF_G0: u32 = 300; /// Likewise for MOVK; no check. pub const R_AARCH64_MOVW_GOTOFF_G0_NC: u32 = 301; /// GOT-rel. o. MOV{N,Z} imm. 31:16. pub const R_AARCH64_MOVW_GOTOFF_G1: u32 = 302; /// Likewise for MOVK; no check. pub const R_AARCH64_MOVW_GOTOFF_G1_NC: u32 = 303; /// GOT-rel. o. MOV{N,Z} imm. 47:32. pub const R_AARCH64_MOVW_GOTOFF_G2: u32 = 304; /// Likewise for MOVK; no check. pub const R_AARCH64_MOVW_GOTOFF_G2_NC: u32 = 305; /// GOT-rel. o. MOV{N,Z} imm. 63:48. pub const R_AARCH64_MOVW_GOTOFF_G3: u32 = 306; /// GOT-relative 64-bit. pub const R_AARCH64_GOTREL64: u32 = 307; /// GOT-relative 32-bit. pub const R_AARCH64_GOTREL32: u32 = 308; /// PC-rel. GOT off. load imm. 20:2. pub const R_AARCH64_GOT_LD_PREL19: u32 = 309; /// GOT-rel. off. LD/ST imm. 14:3. pub const R_AARCH64_LD64_GOTOFF_LO15: u32 = 310; /// P-page-rel. GOT off. ADRP 32:12. pub const R_AARCH64_ADR_GOT_PAGE: u32 = 311; /// Dir. GOT off. LD/ST imm. 11:3. pub const R_AARCH64_LD64_GOT_LO12_NC: u32 = 312; /// GOT-page-rel. GOT off. LD/ST 14:3 pub const R_AARCH64_LD64_GOTPAGE_LO15: u32 = 313; /// PC-relative ADR imm. 20:0. pub const R_AARCH64_TLSGD_ADR_PREL21: u32 = 512; /// page-rel. ADRP imm. 32:12. pub const R_AARCH64_TLSGD_ADR_PAGE21: u32 = 513; /// direct ADD imm. from 11:0. pub const R_AARCH64_TLSGD_ADD_LO12_NC: u32 = 514; /// GOT-rel. MOV{N,Z} 31:16. pub const R_AARCH64_TLSGD_MOVW_G1: u32 = 515; /// GOT-rel. MOVK imm. 15:0. pub const R_AARCH64_TLSGD_MOVW_G0_NC: u32 = 516; /// Like 512; local dynamic model. pub const R_AARCH64_TLSLD_ADR_PREL21: u32 = 517; /// Like 513; local dynamic model. pub const R_AARCH64_TLSLD_ADR_PAGE21: u32 = 518; /// Like 514; local dynamic model. pub const R_AARCH64_TLSLD_ADD_LO12_NC: u32 = 519; /// Like 515; local dynamic model. pub const R_AARCH64_TLSLD_MOVW_G1: u32 = 520; /// Like 516; local dynamic model. pub const R_AARCH64_TLSLD_MOVW_G0_NC: u32 = 521; /// TLS PC-rel. load imm. 20:2. pub const R_AARCH64_TLSLD_LD_PREL19: u32 = 522; /// TLS DTP-rel. MOV{N,Z} 47:32. pub const R_AARCH64_TLSLD_MOVW_DTPREL_G2: u32 = 523; /// TLS DTP-rel. MOV{N,Z} 31:16. pub const R_AARCH64_TLSLD_MOVW_DTPREL_G1: u32 = 524; /// Likewise; MOVK; no check. pub const R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC: u32 = 525; /// TLS DTP-rel. MOV{N,Z} 15:0. pub const R_AARCH64_TLSLD_MOVW_DTPREL_G0: u32 = 526; /// Likewise; MOVK; no check. pub const R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC: u32 = 527; /// DTP-rel. ADD imm. from 23:12. pub const R_AARCH64_TLSLD_ADD_DTPREL_HI12: u32 = 528; /// DTP-rel. ADD imm. from 11:0. pub const R_AARCH64_TLSLD_ADD_DTPREL_LO12: u32 = 529; /// Likewise; no ovfl. check. pub const R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC: u32 = 530; /// DTP-rel. LD/ST imm. 11:0. pub const R_AARCH64_TLSLD_LDST8_DTPREL_LO12: u32 = 531; /// Likewise; no check. pub const R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC: u32 = 532; /// DTP-rel. LD/ST imm. 11:1. pub const R_AARCH64_TLSLD_LDST16_DTPREL_LO12: u32 = 533; /// Likewise; no check. pub const R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC: u32 = 534; /// DTP-rel. LD/ST imm. 11:2. pub const R_AARCH64_TLSLD_LDST32_DTPREL_LO12: u32 = 535; /// Likewise; no check. pub const R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC: u32 = 536; /// DTP-rel. LD/ST imm. 11:3. pub const R_AARCH64_TLSLD_LDST64_DTPREL_LO12: u32 = 537; /// Likewise; no check. pub const R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC: u32 = 538; /// GOT-rel. MOV{N,Z} 31:16. pub const R_AARCH64_TLSIE_MOVW_GOTTPREL_G1: u32 = 539; /// GOT-rel. MOVK 15:0. pub const R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC: u32 = 540; /// Page-rel. ADRP 32:12. pub const R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: u32 = 541; /// Direct LD off. 11:3. pub const R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: u32 = 542; /// PC-rel. load imm. 20:2. pub const R_AARCH64_TLSIE_LD_GOTTPREL_PREL19: u32 = 543; /// TLS TP-rel. MOV{N,Z} 47:32. pub const R_AARCH64_TLSLE_MOVW_TPREL_G2: u32 = 544; /// TLS TP-rel. MOV{N,Z} 31:16. pub const R_AARCH64_TLSLE_MOVW_TPREL_G1: u32 = 545; /// Likewise; MOVK; no check. pub const R_AARCH64_TLSLE_MOVW_TPREL_G1_NC: u32 = 546; /// TLS TP-rel. MOV{N,Z} 15:0. pub const R_AARCH64_TLSLE_MOVW_TPREL_G0: u32 = 547; /// Likewise; MOVK; no check. pub const R_AARCH64_TLSLE_MOVW_TPREL_G0_NC: u32 = 548; /// TP-rel. ADD imm. 23:12. pub const R_AARCH64_TLSLE_ADD_TPREL_HI12: u32 = 549; /// TP-rel. ADD imm. 11:0. pub const R_AARCH64_TLSLE_ADD_TPREL_LO12: u32 = 550; /// Likewise; no ovfl. check. pub const R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: u32 = 551; /// TP-rel. LD/ST off. 11:0. pub const R_AARCH64_TLSLE_LDST8_TPREL_LO12: u32 = 552; /// Likewise; no ovfl. check. pub const R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC: u32 = 553; /// TP-rel. LD/ST off. 11:1. pub const R_AARCH64_TLSLE_LDST16_TPREL_LO12: u32 = 554; /// Likewise; no check. pub const R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC: u32 = 555; /// TP-rel. LD/ST off. 11:2. pub const R_AARCH64_TLSLE_LDST32_TPREL_LO12: u32 = 556; /// Likewise; no check. pub const R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC: u32 = 557; /// TP-rel. LD/ST off. 11:3. pub const R_AARCH64_TLSLE_LDST64_TPREL_LO12: u32 = 558; /// Likewise; no check. pub const R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC: u32 = 559; /// PC-rel. load immediate 20:2. pub const R_AARCH64_TLSDESC_LD_PREL19: u32 = 560; /// PC-rel. ADR immediate 20:0. pub const R_AARCH64_TLSDESC_ADR_PREL21: u32 = 561; /// Page-rel. ADRP imm. 32:12. pub const R_AARCH64_TLSDESC_ADR_PAGE21: u32 = 562; /// Direct LD off. from 11:3. pub const R_AARCH64_TLSDESC_LD64_LO12: u32 = 563; /// Direct ADD imm. from 11:0. pub const R_AARCH64_TLSDESC_ADD_LO12: u32 = 564; /// GOT-rel. MOV{N,Z} imm. 31:16. pub const R_AARCH64_TLSDESC_OFF_G1: u32 = 565; /// GOT-rel. MOVK imm. 15:0; no ck. pub const R_AARCH64_TLSDESC_OFF_G0_NC: u32 = 566; /// Relax LDR. pub const R_AARCH64_TLSDESC_LDR: u32 = 567; /// Relax ADD. pub const R_AARCH64_TLSDESC_ADD: u32 = 568; /// Relax BLR. pub const R_AARCH64_TLSDESC_CALL: u32 = 569; /// TP-rel. LD/ST off. 11:4. pub const R_AARCH64_TLSLE_LDST128_TPREL_LO12: u32 = 570; /// Likewise; no check. pub const R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC: u32 = 571; /// DTP-rel. LD/ST imm. 11:4. pub const R_AARCH64_TLSLD_LDST128_DTPREL_LO12: u32 = 572; /// Likewise; no check. pub const R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC: u32 = 573; /// Copy symbol at runtime. pub const R_AARCH64_COPY: u32 = 1024; /// Create GOT entry. pub const R_AARCH64_GLOB_DAT: u32 = 1025; /// Create PLT entry. pub const R_AARCH64_JUMP_SLOT: u32 = 1026; /// Adjust by program base. pub const R_AARCH64_RELATIVE: u32 = 1027; /// Module number, 64 bit. pub const R_AARCH64_TLS_DTPMOD: u32 = 1028; /// Module-relative offset, 64 bit. pub const R_AARCH64_TLS_DTPREL: u32 = 1029; /// TP-relative offset, 64 bit. pub const R_AARCH64_TLS_TPREL: u32 = 1030; /// TLS Descriptor. pub const R_AARCH64_TLSDESC: u32 = 1031; /// STT_GNU_IFUNC relocation. pub const R_AARCH64_IRELATIVE: u32 = 1032; // AVR values for `FileHeader*::e_flags`. /// Bitmask for `EF_AVR_ARCH_*`. pub const EF_AVR_ARCH: u32 = 0x7F; /// If set, it is assumed that the elf file uses local symbols as reference /// for the relocations so that linker relaxation is possible. pub const EF_AVR_LINKRELAX_PREPARED: u32 = 0x80; pub const EF_AVR_ARCH_AVR1: u32 = 1; pub const EF_AVR_ARCH_AVR2: u32 = 2; pub const EF_AVR_ARCH_AVR25: u32 = 25; pub const EF_AVR_ARCH_AVR3: u32 = 3; pub const EF_AVR_ARCH_AVR31: u32 = 31; pub const EF_AVR_ARCH_AVR35: u32 = 35; pub const EF_AVR_ARCH_AVR4: u32 = 4; pub const EF_AVR_ARCH_AVR5: u32 = 5; pub const EF_AVR_ARCH_AVR51: u32 = 51; pub const EF_AVR_ARCH_AVR6: u32 = 6; pub const EF_AVR_ARCH_AVRTINY: u32 = 100; pub const EF_AVR_ARCH_XMEGA1: u32 = 101; pub const EF_AVR_ARCH_XMEGA2: u32 = 102; pub const EF_AVR_ARCH_XMEGA3: u32 = 103; pub const EF_AVR_ARCH_XMEGA4: u32 = 104; pub const EF_AVR_ARCH_XMEGA5: u32 = 105; pub const EF_AVR_ARCH_XMEGA6: u32 = 106; pub const EF_AVR_ARCH_XMEGA7: u32 = 107; // AVR values for `Rel*::r_type`. pub const R_AVR_NONE: u32 = 0; /// Direct 32 bit pub const R_AVR_32: u32 = 1; pub const R_AVR_7_PCREL: u32 = 2; pub const R_AVR_13_PCREL: u32 = 3; /// Direct 16 bit pub const R_AVR_16: u32 = 4; pub const R_AVR_16_PM: u32 = 5; pub const R_AVR_LO8_LDI: u32 = 6; pub const R_AVR_HI8_LDI: u32 = 7; pub const R_AVR_HH8_LDI: u32 = 8; pub const R_AVR_LO8_LDI_NEG: u32 = 9; pub const R_AVR_HI8_LDI_NEG: u32 = 10; pub const R_AVR_HH8_LDI_NEG: u32 = 11; pub const R_AVR_LO8_LDI_PM: u32 = 12; pub const R_AVR_HI8_LDI_PM: u32 = 13; pub const R_AVR_HH8_LDI_PM: u32 = 14; pub const R_AVR_LO8_LDI_PM_NEG: u32 = 15; pub const R_AVR_HI8_LDI_PM_NEG: u32 = 16; pub const R_AVR_HH8_LDI_PM_NEG: u32 = 17; pub const R_AVR_CALL: u32 = 18; pub const R_AVR_LDI: u32 = 19; pub const R_AVR_6: u32 = 20; pub const R_AVR_6_ADIW: u32 = 21; pub const R_AVR_MS8_LDI: u32 = 22; pub const R_AVR_MS8_LDI_NEG: u32 = 23; pub const R_AVR_LO8_LDI_GS: u32 = 24; pub const R_AVR_HI8_LDI_GS: u32 = 25; pub const R_AVR_8: u32 = 26; pub const R_AVR_8_LO8: u32 = 27; pub const R_AVR_8_HI8: u32 = 28; pub const R_AVR_8_HLO8: u32 = 29; pub const R_AVR_DIFF8: u32 = 30; pub const R_AVR_DIFF16: u32 = 31; pub const R_AVR_DIFF32: u32 = 32; pub const R_AVR_LDS_STS_16: u32 = 33; pub const R_AVR_PORT6: u32 = 34; pub const R_AVR_PORT5: u32 = 35; pub const R_AVR_32_PCREL: u32 = 36; // MSP430 values for `Rel*::r_type`. /// Direct 32 bit pub const R_MSP430_32: u32 = 1; /// Direct 16 bit pub const R_MSP430_16_BYTE: u32 = 5; // Hexagon values for `Rel*::r_type`. /// Direct 32 bit pub const R_HEX_32: u32 = 6; // ARM values for `Rel*::r_type`. /// No reloc pub const R_ARM_NONE: u32 = 0; /// Deprecated PC relative 26 bit branch. pub const R_ARM_PC24: u32 = 1; /// Direct 32 bit pub const R_ARM_ABS32: u32 = 2; /// PC relative 32 bit pub const R_ARM_REL32: u32 = 3; pub const R_ARM_PC13: u32 = 4; /// Direct 16 bit pub const R_ARM_ABS16: u32 = 5; /// Direct 12 bit pub const R_ARM_ABS12: u32 = 6; /// Direct & 0x7C (`LDR`, `STR`). pub const R_ARM_THM_ABS5: u32 = 7; /// Direct 8 bit pub const R_ARM_ABS8: u32 = 8; pub const R_ARM_SBREL32: u32 = 9; /// PC relative 24 bit (Thumb32 `BL`). pub const R_ARM_THM_PC22: u32 = 10; /// PC relative & 0x3FC (Thumb16 `LDR`, `ADD`, `ADR`). pub const R_ARM_THM_PC8: u32 = 11; pub const R_ARM_AMP_VCALL9: u32 = 12; /// Obsolete static relocation. pub const R_ARM_SWI24: u32 = 13; /// Dynamic relocation. pub const R_ARM_TLS_DESC: u32 = 13; /// Reserved. pub const R_ARM_THM_SWI8: u32 = 14; /// Reserved. pub const R_ARM_XPC25: u32 = 15; /// Reserved. pub const R_ARM_THM_XPC22: u32 = 16; /// ID of module containing symbol pub const R_ARM_TLS_DTPMOD32: u32 = 17; /// Offset in TLS block pub const R_ARM_TLS_DTPOFF32: u32 = 18; /// Offset in static TLS block pub const R_ARM_TLS_TPOFF32: u32 = 19; /// Copy symbol at runtime pub const R_ARM_COPY: u32 = 20; /// Create GOT entry pub const R_ARM_GLOB_DAT: u32 = 21; /// Create PLT entry pub const R_ARM_JUMP_SLOT: u32 = 22; /// Adjust by program base pub const R_ARM_RELATIVE: u32 = 23; /// 32 bit offset to GOT pub const R_ARM_GOTOFF: u32 = 24; /// 32 bit PC relative offset to GOT pub const R_ARM_GOTPC: u32 = 25; /// 32 bit GOT entry pub const R_ARM_GOT32: u32 = 26; /// Deprecated, 32 bit PLT address. pub const R_ARM_PLT32: u32 = 27; /// PC relative 24 bit (`BL`, `BLX`). pub const R_ARM_CALL: u32 = 28; /// PC relative 24 bit (`B`, `BL`). pub const R_ARM_JUMP24: u32 = 29; /// PC relative 24 bit (Thumb32 `B.W`). pub const R_ARM_THM_JUMP24: u32 = 30; /// Adjust by program base. pub const R_ARM_BASE_ABS: u32 = 31; /// Obsolete. pub const R_ARM_ALU_PCREL_7_0: u32 = 32; /// Obsolete. pub const R_ARM_ALU_PCREL_15_8: u32 = 33; /// Obsolete. pub const R_ARM_ALU_PCREL_23_15: u32 = 34; /// Deprecated, prog. base relative. pub const R_ARM_LDR_SBREL_11_0: u32 = 35; /// Deprecated, prog. base relative. pub const R_ARM_ALU_SBREL_19_12: u32 = 36; /// Deprecated, prog. base relative. pub const R_ARM_ALU_SBREL_27_20: u32 = 37; pub const R_ARM_TARGET1: u32 = 38; /// Program base relative. pub const R_ARM_SBREL31: u32 = 39; pub const R_ARM_V4BX: u32 = 40; pub const R_ARM_TARGET2: u32 = 41; /// 32 bit PC relative. pub const R_ARM_PREL31: u32 = 42; /// Direct 16-bit (`MOVW`). pub const R_ARM_MOVW_ABS_NC: u32 = 43; /// Direct high 16-bit (`MOVT`). pub const R_ARM_MOVT_ABS: u32 = 44; /// PC relative 16-bit (`MOVW`). pub const R_ARM_MOVW_PREL_NC: u32 = 45; /// PC relative (MOVT). pub const R_ARM_MOVT_PREL: u32 = 46; /// Direct 16 bit (Thumb32 `MOVW`). pub const R_ARM_THM_MOVW_ABS_NC: u32 = 47; /// Direct high 16 bit (Thumb32 `MOVT`). pub const R_ARM_THM_MOVT_ABS: u32 = 48; /// PC relative 16 bit (Thumb32 `MOVW`). pub const R_ARM_THM_MOVW_PREL_NC: u32 = 49; /// PC relative high 16 bit (Thumb32 `MOVT`). pub const R_ARM_THM_MOVT_PREL: u32 = 50; /// PC relative 20 bit (Thumb32 `B.W`). pub const R_ARM_THM_JUMP19: u32 = 51; /// PC relative X & 0x7E (Thumb16 `CBZ`, `CBNZ`). pub const R_ARM_THM_JUMP6: u32 = 52; /// PC relative 12 bit (Thumb32 `ADR.W`). pub const R_ARM_THM_ALU_PREL_11_0: u32 = 53; /// PC relative 12 bit (Thumb32 `LDR{D,SB,H,SH}`). pub const R_ARM_THM_PC12: u32 = 54; /// Direct 32-bit. pub const R_ARM_ABS32_NOI: u32 = 55; /// PC relative 32-bit. pub const R_ARM_REL32_NOI: u32 = 56; /// PC relative (`ADD`, `SUB`). pub const R_ARM_ALU_PC_G0_NC: u32 = 57; /// PC relative (`ADD`, `SUB`). pub const R_ARM_ALU_PC_G0: u32 = 58; /// PC relative (`ADD`, `SUB`). pub const R_ARM_ALU_PC_G1_NC: u32 = 59; /// PC relative (`ADD`, `SUB`). pub const R_ARM_ALU_PC_G1: u32 = 60; /// PC relative (`ADD`, `SUB`). pub const R_ARM_ALU_PC_G2: u32 = 61; /// PC relative (`LDR`,`STR`,`LDRB`,`STRB`). pub const R_ARM_LDR_PC_G1: u32 = 62; /// PC relative (`LDR`,`STR`,`LDRB`,`STRB`). pub const R_ARM_LDR_PC_G2: u32 = 63; /// PC relative (`STR{D,H}`, `LDR{D,SB,H,SH}`). pub const R_ARM_LDRS_PC_G0: u32 = 64; /// PC relative (`STR{D,H}`, `LDR{D,SB,H,SH}`). pub const R_ARM_LDRS_PC_G1: u32 = 65; /// PC relative (`STR{D,H}`, `LDR{D,SB,H,SH}`). pub const R_ARM_LDRS_PC_G2: u32 = 66; /// PC relative (`LDC`, `STC`). pub const R_ARM_LDC_PC_G0: u32 = 67; /// PC relative (`LDC`, `STC`). pub const R_ARM_LDC_PC_G1: u32 = 68; /// PC relative (`LDC`, `STC`). pub const R_ARM_LDC_PC_G2: u32 = 69; /// Program base relative (`ADD`,`SUB`). pub const R_ARM_ALU_SB_G0_NC: u32 = 70; /// Program base relative (`ADD`,`SUB`). pub const R_ARM_ALU_SB_G0: u32 = 71; /// Program base relative (`ADD`,`SUB`). pub const R_ARM_ALU_SB_G1_NC: u32 = 72; /// Program base relative (`ADD`,`SUB`). pub const R_ARM_ALU_SB_G1: u32 = 73; /// Program base relative (`ADD`,`SUB`). pub const R_ARM_ALU_SB_G2: u32 = 74; /// Program base relative (`LDR`, `STR`, `LDRB`, `STRB`). pub const R_ARM_LDR_SB_G0: u32 = 75; /// Program base relative (`LDR`, `STR`, `LDRB`, `STRB`). pub const R_ARM_LDR_SB_G1: u32 = 76; /// Program base relative (`LDR`, `STR`, `LDRB`, `STRB`). pub const R_ARM_LDR_SB_G2: u32 = 77; /// Program base relative (`LDR`, `STR`, `LDRB`, `STRB`). pub const R_ARM_LDRS_SB_G0: u32 = 78; /// Program base relative (`LDR`, `STR`, `LDRB`, `STRB`). pub const R_ARM_LDRS_SB_G1: u32 = 79; /// Program base relative (`LDR`, `STR`, `LDRB`, `STRB`). pub const R_ARM_LDRS_SB_G2: u32 = 80; /// Program base relative (`LDC`,`STC`). pub const R_ARM_LDC_SB_G0: u32 = 81; /// Program base relative (`LDC`,`STC`). pub const R_ARM_LDC_SB_G1: u32 = 82; /// Program base relative (`LDC`,`STC`). pub const R_ARM_LDC_SB_G2: u32 = 83; /// Program base relative 16 bit (`MOVW`). pub const R_ARM_MOVW_BREL_NC: u32 = 84; /// Program base relative high 16 bit (`MOVT`). pub const R_ARM_MOVT_BREL: u32 = 85; /// Program base relative 16 bit (`MOVW`). pub const R_ARM_MOVW_BREL: u32 = 86; /// Program base relative 16 bit (Thumb32 `MOVW`). pub const R_ARM_THM_MOVW_BREL_NC: u32 = 87; /// Program base relative high 16 bit (Thumb32 `MOVT`). pub const R_ARM_THM_MOVT_BREL: u32 = 88; /// Program base relative 16 bit (Thumb32 `MOVW`). pub const R_ARM_THM_MOVW_BREL: u32 = 89; pub const R_ARM_TLS_GOTDESC: u32 = 90; pub const R_ARM_TLS_CALL: u32 = 91; /// TLS relaxation. pub const R_ARM_TLS_DESCSEQ: u32 = 92; pub const R_ARM_THM_TLS_CALL: u32 = 93; pub const R_ARM_PLT32_ABS: u32 = 94; /// GOT entry. pub const R_ARM_GOT_ABS: u32 = 95; /// PC relative GOT entry. pub const R_ARM_GOT_PREL: u32 = 96; /// GOT entry relative to GOT origin (`LDR`). pub const R_ARM_GOT_BREL12: u32 = 97; /// 12 bit, GOT entry relative to GOT origin (`LDR`, `STR`). pub const R_ARM_GOTOFF12: u32 = 98; pub const R_ARM_GOTRELAX: u32 = 99; pub const R_ARM_GNU_VTENTRY: u32 = 100; pub const R_ARM_GNU_VTINHERIT: u32 = 101; /// PC relative & 0xFFE (Thumb16 `B`). pub const R_ARM_THM_PC11: u32 = 102; /// PC relative & 0x1FE (Thumb16 `B`/`B`). pub const R_ARM_THM_PC9: u32 = 103; /// PC-rel 32 bit for global dynamic thread local data pub const R_ARM_TLS_GD32: u32 = 104; /// PC-rel 32 bit for local dynamic thread local data pub const R_ARM_TLS_LDM32: u32 = 105; /// 32 bit offset relative to TLS block pub const R_ARM_TLS_LDO32: u32 = 106; /// PC-rel 32 bit for GOT entry of static TLS block offset pub const R_ARM_TLS_IE32: u32 = 107; /// 32 bit offset relative to static TLS block pub const R_ARM_TLS_LE32: u32 = 108; /// 12 bit relative to TLS block (`LDR`, `STR`). pub const R_ARM_TLS_LDO12: u32 = 109; /// 12 bit relative to static TLS block (`LDR`, `STR`). pub const R_ARM_TLS_LE12: u32 = 110; /// 12 bit GOT entry relative to GOT origin (`LDR`). pub const R_ARM_TLS_IE12GP: u32 = 111; /// Obsolete. pub const R_ARM_ME_TOO: u32 = 128; pub const R_ARM_THM_TLS_DESCSEQ: u32 = 129; pub const R_ARM_THM_TLS_DESCSEQ16: u32 = 129; pub const R_ARM_THM_TLS_DESCSEQ32: u32 = 130; /// GOT entry relative to GOT origin, 12 bit (Thumb32 `LDR`). pub const R_ARM_THM_GOT_BREL12: u32 = 131; pub const R_ARM_IRELATIVE: u32 = 160; pub const R_ARM_RXPC25: u32 = 249; pub const R_ARM_RSBREL32: u32 = 250; pub const R_ARM_THM_RPC22: u32 = 251; pub const R_ARM_RREL32: u32 = 252; pub const R_ARM_RABS22: u32 = 253; pub const R_ARM_RPC24: u32 = 254; pub const R_ARM_RBASE: u32 = 255; // C-SKY values for `Rel*::r_type`. /// no reloc pub const R_CKCORE_NONE: u32 = 0; /// direct 32 bit (S + A) pub const R_CKCORE_ADDR32: u32 = 1; /// disp ((S + A - P) >> 2) & 0xff pub const R_CKCORE_PCRELIMM8BY4: u32 = 2; /// disp ((S + A - P) >> 1) & 0x7ff pub const R_CKCORE_PCRELIMM11BY2: u32 = 3; /// 32-bit rel (S + A - P) pub const R_CKCORE_PCREL32: u32 = 5; /// disp ((S + A - P) >>1) & 0x7ff pub const R_CKCORE_PCRELJSR_IMM11BY2: u32 = 6; /// 32 bit adjust program base(B + A) pub const R_CKCORE_RELATIVE: u32 = 9; /// 32 bit adjust by program base pub const R_CKCORE_COPY: u32 = 10; /// off between got and sym (S) pub const R_CKCORE_GLOB_DAT: u32 = 11; /// PLT entry (S) pub const R_CKCORE_JUMP_SLOT: u32 = 12; /// offset to GOT (S + A - GOT) pub const R_CKCORE_GOTOFF: u32 = 13; /// PC offset to GOT (GOT + A - P) pub const R_CKCORE_GOTPC: u32 = 14; /// 32 bit GOT entry (G) pub const R_CKCORE_GOT32: u32 = 15; /// 32 bit PLT entry (G) pub const R_CKCORE_PLT32: u32 = 16; /// GOT entry in GLOB_DAT (GOT + G) pub const R_CKCORE_ADDRGOT: u32 = 17; /// PLT entry in GLOB_DAT (GOT + G) pub const R_CKCORE_ADDRPLT: u32 = 18; /// ((S + A - P) >> 1) & 0x3ff_ffff pub const R_CKCORE_PCREL_IMM26BY2: u32 = 19; /// disp ((S + A - P) >> 1) & 0xffff pub const R_CKCORE_PCREL_IMM16BY2: u32 = 20; /// disp ((S + A - P) >> 2) & 0xffff pub const R_CKCORE_PCREL_IMM16BY4: u32 = 21; /// disp ((S + A - P) >> 1) & 0x3ff pub const R_CKCORE_PCREL_IMM10BY2: u32 = 22; /// disp ((S + A - P) >> 2) & 0x3ff pub const R_CKCORE_PCREL_IMM10BY4: u32 = 23; /// high & low 16 bit ADDR, ((S + A) >> 16) & 0xffff pub const R_CKCORE_ADDR_HI16: u32 = 24; /// (S + A) & 0xffff pub const R_CKCORE_ADDR_LO16: u32 = 25; /// high & low 16 bit GOTPC, ((GOT + A - P) >> 16) & 0xffff pub const R_CKCORE_GOTPC_HI16: u32 = 26; /// (GOT + A - P) & 0xffff pub const R_CKCORE_GOTPC_LO16: u32 = 27; /// high & low 16 bit GOTOFF, ((S + A - GOT) >> 16) & 0xffff pub const R_CKCORE_GOTOFF_HI16: u32 = 28; /// (S + A - GOT) & 0xffff pub const R_CKCORE_GOTOFF_LO16: u32 = 29; /// 12 bit disp GOT entry (G) pub const R_CKCORE_GOT12: u32 = 30; /// high & low 16 bit GOT, (G >> 16) & 0xffff pub const R_CKCORE_GOT_HI16: u32 = 31; /// (G & 0xffff) pub const R_CKCORE_GOT_LO16: u32 = 32; /// 12 bit disp PLT entry (G) pub const R_CKCORE_PLT12: u32 = 33; /// high & low 16 bit PLT, (G >> 16) & 0xffff pub const R_CKCORE_PLT_HI16: u32 = 34; /// G & 0xffff pub const R_CKCORE_PLT_LO16: u32 = 35; /// high & low 16 bit ADDRGOT, (GOT + G * 4) & 0xffff pub const R_CKCORE_ADDRGOT_HI16: u32 = 36; /// (GOT + G * 4) & 0xffff pub const R_CKCORE_ADDRGOT_LO16: u32 = 37; /// high & low 16 bit ADDRPLT, ((GOT + G * 4) >> 16) & 0xFFFF pub const R_CKCORE_ADDRPLT_HI16: u32 = 38; /// (GOT+G*4) & 0xffff pub const R_CKCORE_ADDRPLT_LO16: u32 = 39; /// disp ((S+A-P) >>1) & x3ff_ffff pub const R_CKCORE_PCREL_JSR_IMM26BY2: u32 = 40; /// (S+A-BTEXT) & 0xffff pub const R_CKCORE_TOFFSET_LO16: u32 = 41; /// (S+A-BTEXT) & 0xffff pub const R_CKCORE_DOFFSET_LO16: u32 = 42; /// disp ((S+A-P) >>1) & 0x3ffff pub const R_CKCORE_PCREL_IMM18BY2: u32 = 43; /// disp (S+A-BDATA) & 0x3ffff pub const R_CKCORE_DOFFSET_IMM18: u32 = 44; /// disp ((S+A-BDATA)>>1) & 0x3ffff pub const R_CKCORE_DOFFSET_IMM18BY2: u32 = 45; /// disp ((S+A-BDATA)>>2) & 0x3ffff pub const R_CKCORE_DOFFSET_IMM18BY4: u32 = 46; /// disp (G >> 2) pub const R_CKCORE_GOT_IMM18BY4: u32 = 48; /// disp (G >> 2) pub const R_CKCORE_PLT_IMM18BY4: u32 = 49; /// disp ((S+A-P) >>2) & 0x7f pub const R_CKCORE_PCREL_IMM7BY4: u32 = 50; /// 32 bit offset to TLS block pub const R_CKCORE_TLS_LE32: u32 = 51; pub const R_CKCORE_TLS_IE32: u32 = 52; pub const R_CKCORE_TLS_GD32: u32 = 53; pub const R_CKCORE_TLS_LDM32: u32 = 54; pub const R_CKCORE_TLS_LDO32: u32 = 55; pub const R_CKCORE_TLS_DTPMOD32: u32 = 56; pub const R_CKCORE_TLS_DTPOFF32: u32 = 57; pub const R_CKCORE_TLS_TPOFF32: u32 = 58; // C-SKY values for `FileHeader*::e_flags`. pub const EF_CSKY_ABIMASK: u32 = 0xF000_0000; pub const EF_CSKY_OTHER: u32 = 0x0FFF_0000; pub const EF_CSKY_PROCESSOR: u32 = 0x0000_FFFF; pub const EF_CSKY_ABIV1: u32 = 0x1000_0000; pub const EF_CSKY_ABIV2: u32 = 0x2000_0000; // C-SKY values for `SectionHeader*::sh_type`. /// C-SKY attributes section. pub const SHT_CSKY_ATTRIBUTES: u32 = SHT_LOPROC + 1; // IA-64 specific declarations. // IA-64 values for `FileHeader64::e_flags`. /// os-specific flags pub const EF_IA_64_MASKOS: u32 = 0x0000_000f; /// 64-bit ABI pub const EF_IA_64_ABI64: u32 = 0x0000_0010; /// arch. version mask pub const EF_IA_64_ARCH: u32 = 0xff00_0000; // IA-64 values for `ProgramHeader64::p_type`. /// arch extension bits pub const PT_IA_64_ARCHEXT: u32 = PT_LOPROC + 0; /// ia64 unwind bits pub const PT_IA_64_UNWIND: u32 = PT_LOPROC + 1; pub const PT_IA_64_HP_OPT_ANOT: u32 = PT_LOOS + 0x12; pub const PT_IA_64_HP_HSL_ANOT: u32 = PT_LOOS + 0x13; pub const PT_IA_64_HP_STACK: u32 = PT_LOOS + 0x14; // IA-64 values for `ProgramHeader64::p_flags`. /// spec insns w/o recovery pub const PF_IA_64_NORECOV: u32 = 0x8000_0000; // IA-64 values for `SectionHeader64::sh_type`. /// extension bits pub const SHT_IA_64_EXT: u32 = SHT_LOPROC + 0; /// unwind bits pub const SHT_IA_64_UNWIND: u32 = SHT_LOPROC + 1; // IA-64 values for `SectionHeader64::sh_flags`. /// section near gp pub const SHF_IA_64_SHORT: u32 = 0x1000_0000; /// spec insns w/o recovery pub const SHF_IA_64_NORECOV: u32 = 0x2000_0000; // IA-64 values for `Dyn64::d_tag`. pub const DT_IA_64_PLT_RESERVE: u32 = DT_LOPROC + 0; // IA-64 values for `Rel*::r_type`. /// none pub const R_IA64_NONE: u32 = 0x00; /// symbol + addend, add imm14 pub const R_IA64_IMM14: u32 = 0x21; /// symbol + addend, add imm22 pub const R_IA64_IMM22: u32 = 0x22; /// symbol + addend, mov imm64 pub const R_IA64_IMM64: u32 = 0x23; /// symbol + addend, data4 MSB pub const R_IA64_DIR32MSB: u32 = 0x24; /// symbol + addend, data4 LSB pub const R_IA64_DIR32LSB: u32 = 0x25; /// symbol + addend, data8 MSB pub const R_IA64_DIR64MSB: u32 = 0x26; /// symbol + addend, data8 LSB pub const R_IA64_DIR64LSB: u32 = 0x27; /// @gprel(sym + add), add imm22 pub const R_IA64_GPREL22: u32 = 0x2a; /// @gprel(sym + add), mov imm64 pub const R_IA64_GPREL64I: u32 = 0x2b; /// @gprel(sym + add), data4 MSB pub const R_IA64_GPREL32MSB: u32 = 0x2c; /// @gprel(sym + add), data4 LSB pub const R_IA64_GPREL32LSB: u32 = 0x2d; /// @gprel(sym + add), data8 MSB pub const R_IA64_GPREL64MSB: u32 = 0x2e; /// @gprel(sym + add), data8 LSB pub const R_IA64_GPREL64LSB: u32 = 0x2f; /// @ltoff(sym + add), add imm22 pub const R_IA64_LTOFF22: u32 = 0x32; /// @ltoff(sym + add), mov imm64 pub const R_IA64_LTOFF64I: u32 = 0x33; /// @pltoff(sym + add), add imm22 pub const R_IA64_PLTOFF22: u32 = 0x3a; /// @pltoff(sym + add), mov imm64 pub const R_IA64_PLTOFF64I: u32 = 0x3b; /// @pltoff(sym + add), data8 MSB pub const R_IA64_PLTOFF64MSB: u32 = 0x3e; /// @pltoff(sym + add), data8 LSB pub const R_IA64_PLTOFF64LSB: u32 = 0x3f; /// @fptr(sym + add), mov imm64 pub const R_IA64_FPTR64I: u32 = 0x43; /// @fptr(sym + add), data4 MSB pub const R_IA64_FPTR32MSB: u32 = 0x44; /// @fptr(sym + add), data4 LSB pub const R_IA64_FPTR32LSB: u32 = 0x45; /// @fptr(sym + add), data8 MSB pub const R_IA64_FPTR64MSB: u32 = 0x46; /// @fptr(sym + add), data8 LSB pub const R_IA64_FPTR64LSB: u32 = 0x47; /// @pcrel(sym + add), brl pub const R_IA64_PCREL60B: u32 = 0x48; /// @pcrel(sym + add), ptb, call pub const R_IA64_PCREL21B: u32 = 0x49; /// @pcrel(sym + add), chk.s pub const R_IA64_PCREL21M: u32 = 0x4a; /// @pcrel(sym + add), fchkf pub const R_IA64_PCREL21F: u32 = 0x4b; /// @pcrel(sym + add), data4 MSB pub const R_IA64_PCREL32MSB: u32 = 0x4c; /// @pcrel(sym + add), data4 LSB pub const R_IA64_PCREL32LSB: u32 = 0x4d; /// @pcrel(sym + add), data8 MSB pub const R_IA64_PCREL64MSB: u32 = 0x4e; /// @pcrel(sym + add), data8 LSB pub const R_IA64_PCREL64LSB: u32 = 0x4f; /// @ltoff(@fptr(s+a)), imm22 pub const R_IA64_LTOFF_FPTR22: u32 = 0x52; /// @ltoff(@fptr(s+a)), imm64 pub const R_IA64_LTOFF_FPTR64I: u32 = 0x53; /// @ltoff(@fptr(s+a)), data4 MSB pub const R_IA64_LTOFF_FPTR32MSB: u32 = 0x54; /// @ltoff(@fptr(s+a)), data4 LSB pub const R_IA64_LTOFF_FPTR32LSB: u32 = 0x55; /// @ltoff(@fptr(s+a)), data8 MSB pub const R_IA64_LTOFF_FPTR64MSB: u32 = 0x56; /// @ltoff(@fptr(s+a)), data8 LSB pub const R_IA64_LTOFF_FPTR64LSB: u32 = 0x57; /// @segrel(sym + add), data4 MSB pub const R_IA64_SEGREL32MSB: u32 = 0x5c; /// @segrel(sym + add), data4 LSB pub const R_IA64_SEGREL32LSB: u32 = 0x5d; /// @segrel(sym + add), data8 MSB pub const R_IA64_SEGREL64MSB: u32 = 0x5e; /// @segrel(sym + add), data8 LSB pub const R_IA64_SEGREL64LSB: u32 = 0x5f; /// @secrel(sym + add), data4 MSB pub const R_IA64_SECREL32MSB: u32 = 0x64; /// @secrel(sym + add), data4 LSB pub const R_IA64_SECREL32LSB: u32 = 0x65; /// @secrel(sym + add), data8 MSB pub const R_IA64_SECREL64MSB: u32 = 0x66; /// @secrel(sym + add), data8 LSB pub const R_IA64_SECREL64LSB: u32 = 0x67; /// data 4 + REL pub const R_IA64_REL32MSB: u32 = 0x6c; /// data 4 + REL pub const R_IA64_REL32LSB: u32 = 0x6d; /// data 8 + REL pub const R_IA64_REL64MSB: u32 = 0x6e; /// data 8 + REL pub const R_IA64_REL64LSB: u32 = 0x6f; /// symbol + addend, data4 MSB pub const R_IA64_LTV32MSB: u32 = 0x74; /// symbol + addend, data4 LSB pub const R_IA64_LTV32LSB: u32 = 0x75; /// symbol + addend, data8 MSB pub const R_IA64_LTV64MSB: u32 = 0x76; /// symbol + addend, data8 LSB pub const R_IA64_LTV64LSB: u32 = 0x77; /// @pcrel(sym + add), 21bit inst pub const R_IA64_PCREL21BI: u32 = 0x79; /// @pcrel(sym + add), 22bit inst pub const R_IA64_PCREL22: u32 = 0x7a; /// @pcrel(sym + add), 64bit inst pub const R_IA64_PCREL64I: u32 = 0x7b; /// dynamic reloc, imported PLT, MSB pub const R_IA64_IPLTMSB: u32 = 0x80; /// dynamic reloc, imported PLT, LSB pub const R_IA64_IPLTLSB: u32 = 0x81; /// copy relocation pub const R_IA64_COPY: u32 = 0x84; /// Addend and symbol difference pub const R_IA64_SUB: u32 = 0x85; /// LTOFF22, relaxable. pub const R_IA64_LTOFF22X: u32 = 0x86; /// Use of LTOFF22X. pub const R_IA64_LDXMOV: u32 = 0x87; /// @tprel(sym + add), imm14 pub const R_IA64_TPREL14: u32 = 0x91; /// @tprel(sym + add), imm22 pub const R_IA64_TPREL22: u32 = 0x92; /// @tprel(sym + add), imm64 pub const R_IA64_TPREL64I: u32 = 0x93; /// @tprel(sym + add), data8 MSB pub const R_IA64_TPREL64MSB: u32 = 0x96; /// @tprel(sym + add), data8 LSB pub const R_IA64_TPREL64LSB: u32 = 0x97; /// @ltoff(@tprel(s+a)), imm2 pub const R_IA64_LTOFF_TPREL22: u32 = 0x9a; /// @dtpmod(sym + add), data8 MSB pub const R_IA64_DTPMOD64MSB: u32 = 0xa6; /// @dtpmod(sym + add), data8 LSB pub const R_IA64_DTPMOD64LSB: u32 = 0xa7; /// @ltoff(@dtpmod(sym + add)), imm22 pub const R_IA64_LTOFF_DTPMOD22: u32 = 0xaa; /// @dtprel(sym + add), imm14 pub const R_IA64_DTPREL14: u32 = 0xb1; /// @dtprel(sym + add), imm22 pub const R_IA64_DTPREL22: u32 = 0xb2; /// @dtprel(sym + add), imm64 pub const R_IA64_DTPREL64I: u32 = 0xb3; /// @dtprel(sym + add), data4 MSB pub const R_IA64_DTPREL32MSB: u32 = 0xb4; /// @dtprel(sym + add), data4 LSB pub const R_IA64_DTPREL32LSB: u32 = 0xb5; /// @dtprel(sym + add), data8 MSB pub const R_IA64_DTPREL64MSB: u32 = 0xb6; /// @dtprel(sym + add), data8 LSB pub const R_IA64_DTPREL64LSB: u32 = 0xb7; /// @ltoff(@dtprel(s+a)), imm22 pub const R_IA64_LTOFF_DTPREL22: u32 = 0xba; // SH specific declarations. // SH values `FileHeader*::e_flags`. pub const EF_SH_MACH_MASK: u32 = 0x1f; pub const EF_SH_UNKNOWN: u32 = 0x0; pub const EF_SH1: u32 = 0x1; pub const EF_SH2: u32 = 0x2; pub const EF_SH3: u32 = 0x3; pub const EF_SH_DSP: u32 = 0x4; pub const EF_SH3_DSP: u32 = 0x5; pub const EF_SH4AL_DSP: u32 = 0x6; pub const EF_SH3E: u32 = 0x8; pub const EF_SH4: u32 = 0x9; pub const EF_SH2E: u32 = 0xb; pub const EF_SH4A: u32 = 0xc; pub const EF_SH2A: u32 = 0xd; pub const EF_SH4_NOFPU: u32 = 0x10; pub const EF_SH4A_NOFPU: u32 = 0x11; pub const EF_SH4_NOMMU_NOFPU: u32 = 0x12; pub const EF_SH2A_NOFPU: u32 = 0x13; pub const EF_SH3_NOMMU: u32 = 0x14; pub const EF_SH2A_SH4_NOFPU: u32 = 0x15; pub const EF_SH2A_SH3_NOFPU: u32 = 0x16; pub const EF_SH2A_SH4: u32 = 0x17; pub const EF_SH2A_SH3E: u32 = 0x18; // SH values `Rel*::r_type`. pub const R_SH_NONE: u32 = 0; pub const R_SH_DIR32: u32 = 1; pub const R_SH_REL32: u32 = 2; pub const R_SH_DIR8WPN: u32 = 3; pub const R_SH_IND12W: u32 = 4; pub const R_SH_DIR8WPL: u32 = 5; pub const R_SH_DIR8WPZ: u32 = 6; pub const R_SH_DIR8BP: u32 = 7; pub const R_SH_DIR8W: u32 = 8; pub const R_SH_DIR8L: u32 = 9; pub const R_SH_SWITCH16: u32 = 25; pub const R_SH_SWITCH32: u32 = 26; pub const R_SH_USES: u32 = 27; pub const R_SH_COUNT: u32 = 28; pub const R_SH_ALIGN: u32 = 29; pub const R_SH_CODE: u32 = 30; pub const R_SH_DATA: u32 = 31; pub const R_SH_LABEL: u32 = 32; pub const R_SH_SWITCH8: u32 = 33; pub const R_SH_GNU_VTINHERIT: u32 = 34; pub const R_SH_GNU_VTENTRY: u32 = 35; pub const R_SH_TLS_GD_32: u32 = 144; pub const R_SH_TLS_LD_32: u32 = 145; pub const R_SH_TLS_LDO_32: u32 = 146; pub const R_SH_TLS_IE_32: u32 = 147; pub const R_SH_TLS_LE_32: u32 = 148; pub const R_SH_TLS_DTPMOD32: u32 = 149; pub const R_SH_TLS_DTPOFF32: u32 = 150; pub const R_SH_TLS_TPOFF32: u32 = 151; pub const R_SH_GOT32: u32 = 160; pub const R_SH_PLT32: u32 = 161; pub const R_SH_COPY: u32 = 162; pub const R_SH_GLOB_DAT: u32 = 163; pub const R_SH_JMP_SLOT: u32 = 164; pub const R_SH_RELATIVE: u32 = 165; pub const R_SH_GOTOFF: u32 = 166; pub const R_SH_GOTPC: u32 = 167; // S/390 specific definitions. // S/390 values `FileHeader*::e_flags`. /// High GPRs kernel facility needed. pub const EF_S390_HIGH_GPRS: u32 = 0x0000_0001; // S/390 values `Rel*::r_type`. /// No reloc. pub const R_390_NONE: u32 = 0; /// Direct 8 bit. pub const R_390_8: u32 = 1; /// Direct 12 bit. pub const R_390_12: u32 = 2; /// Direct 16 bit. pub const R_390_16: u32 = 3; /// Direct 32 bit. pub const R_390_32: u32 = 4; /// PC relative 32 bit. pub const R_390_PC32: u32 = 5; /// 12 bit GOT offset. pub const R_390_GOT12: u32 = 6; /// 32 bit GOT offset. pub const R_390_GOT32: u32 = 7; /// 32 bit PC relative PLT address. pub const R_390_PLT32: u32 = 8; /// Copy symbol at runtime. pub const R_390_COPY: u32 = 9; /// Create GOT entry. pub const R_390_GLOB_DAT: u32 = 10; /// Create PLT entry. pub const R_390_JMP_SLOT: u32 = 11; /// Adjust by program base. pub const R_390_RELATIVE: u32 = 12; /// 32 bit offset to GOT. pub const R_390_GOTOFF32: u32 = 13; /// 32 bit PC relative offset to GOT. pub const R_390_GOTPC: u32 = 14; /// 16 bit GOT offset. pub const R_390_GOT16: u32 = 15; /// PC relative 16 bit. pub const R_390_PC16: u32 = 16; /// PC relative 16 bit shifted by 1. pub const R_390_PC16DBL: u32 = 17; /// 16 bit PC rel. PLT shifted by 1. pub const R_390_PLT16DBL: u32 = 18; /// PC relative 32 bit shifted by 1. pub const R_390_PC32DBL: u32 = 19; /// 32 bit PC rel. PLT shifted by 1. pub const R_390_PLT32DBL: u32 = 20; /// 32 bit PC rel. GOT shifted by 1. pub const R_390_GOTPCDBL: u32 = 21; /// Direct 64 bit. pub const R_390_64: u32 = 22; /// PC relative 64 bit. pub const R_390_PC64: u32 = 23; /// 64 bit GOT offset. pub const R_390_GOT64: u32 = 24; /// 64 bit PC relative PLT address. pub const R_390_PLT64: u32 = 25; /// 32 bit PC rel. to GOT entry >> 1. pub const R_390_GOTENT: u32 = 26; /// 16 bit offset to GOT. pub const R_390_GOTOFF16: u32 = 27; /// 64 bit offset to GOT. pub const R_390_GOTOFF64: u32 = 28; /// 12 bit offset to jump slot. pub const R_390_GOTPLT12: u32 = 29; /// 16 bit offset to jump slot. pub const R_390_GOTPLT16: u32 = 30; /// 32 bit offset to jump slot. pub const R_390_GOTPLT32: u32 = 31; /// 64 bit offset to jump slot. pub const R_390_GOTPLT64: u32 = 32; /// 32 bit rel. offset to jump slot. pub const R_390_GOTPLTENT: u32 = 33; /// 16 bit offset from GOT to PLT. pub const R_390_PLTOFF16: u32 = 34; /// 32 bit offset from GOT to PLT. pub const R_390_PLTOFF32: u32 = 35; /// 16 bit offset from GOT to PLT. pub const R_390_PLTOFF64: u32 = 36; /// Tag for load insn in TLS code. pub const R_390_TLS_LOAD: u32 = 37; /// Tag for function call in general dynamic TLS code. pub const R_390_TLS_GDCALL: u32 = 38; /// Tag for function call in local dynamic TLS code. pub const R_390_TLS_LDCALL: u32 = 39; /// Direct 32 bit for general dynamic thread local data. pub const R_390_TLS_GD32: u32 = 40; /// Direct 64 bit for general dynamic thread local data. pub const R_390_TLS_GD64: u32 = 41; /// 12 bit GOT offset for static TLS block offset. pub const R_390_TLS_GOTIE12: u32 = 42; /// 32 bit GOT offset for static TLS block offset. pub const R_390_TLS_GOTIE32: u32 = 43; /// 64 bit GOT offset for static TLS block offset. pub const R_390_TLS_GOTIE64: u32 = 44; /// Direct 32 bit for local dynamic thread local data in LE code. pub const R_390_TLS_LDM32: u32 = 45; /// Direct 64 bit for local dynamic thread local data in LE code. pub const R_390_TLS_LDM64: u32 = 46; /// 32 bit address of GOT entry for negated static TLS block offset. pub const R_390_TLS_IE32: u32 = 47; /// 64 bit address of GOT entry for negated static TLS block offset. pub const R_390_TLS_IE64: u32 = 48; /// 32 bit rel. offset to GOT entry for negated static TLS block offset. pub const R_390_TLS_IEENT: u32 = 49; /// 32 bit negated offset relative to static TLS block. pub const R_390_TLS_LE32: u32 = 50; /// 64 bit negated offset relative to static TLS block. pub const R_390_TLS_LE64: u32 = 51; /// 32 bit offset relative to TLS block. pub const R_390_TLS_LDO32: u32 = 52; /// 64 bit offset relative to TLS block. pub const R_390_TLS_LDO64: u32 = 53; /// ID of module containing symbol. pub const R_390_TLS_DTPMOD: u32 = 54; /// Offset in TLS block. pub const R_390_TLS_DTPOFF: u32 = 55; /// Negated offset in static TLS block. pub const R_390_TLS_TPOFF: u32 = 56; /// Direct 20 bit. pub const R_390_20: u32 = 57; /// 20 bit GOT offset. pub const R_390_GOT20: u32 = 58; /// 20 bit offset to jump slot. pub const R_390_GOTPLT20: u32 = 59; /// 20 bit GOT offset for static TLS block offset. pub const R_390_TLS_GOTIE20: u32 = 60; /// STT_GNU_IFUNC relocation. pub const R_390_IRELATIVE: u32 = 61; // CRIS values `Rel*::r_type`. pub const R_CRIS_NONE: u32 = 0; pub const R_CRIS_8: u32 = 1; pub const R_CRIS_16: u32 = 2; pub const R_CRIS_32: u32 = 3; pub const R_CRIS_8_PCREL: u32 = 4; pub const R_CRIS_16_PCREL: u32 = 5; pub const R_CRIS_32_PCREL: u32 = 6; pub const R_CRIS_GNU_VTINHERIT: u32 = 7; pub const R_CRIS_GNU_VTENTRY: u32 = 8; pub const R_CRIS_COPY: u32 = 9; pub const R_CRIS_GLOB_DAT: u32 = 10; pub const R_CRIS_JUMP_SLOT: u32 = 11; pub const R_CRIS_RELATIVE: u32 = 12; pub const R_CRIS_16_GOT: u32 = 13; pub const R_CRIS_32_GOT: u32 = 14; pub const R_CRIS_16_GOTPLT: u32 = 15; pub const R_CRIS_32_GOTPLT: u32 = 16; pub const R_CRIS_32_GOTREL: u32 = 17; pub const R_CRIS_32_PLT_GOTREL: u32 = 18; pub const R_CRIS_32_PLT_PCREL: u32 = 19; // AMD x86-64 values `Rel*::r_type`. /// No reloc pub const R_X86_64_NONE: u32 = 0; /// Direct 64 bit pub const R_X86_64_64: u32 = 1; /// PC relative 32 bit signed pub const R_X86_64_PC32: u32 = 2; /// 32 bit GOT entry pub const R_X86_64_GOT32: u32 = 3; /// 32 bit PLT address pub const R_X86_64_PLT32: u32 = 4; /// Copy symbol at runtime pub const R_X86_64_COPY: u32 = 5; /// Create GOT entry pub const R_X86_64_GLOB_DAT: u32 = 6; /// Create PLT entry pub const R_X86_64_JUMP_SLOT: u32 = 7; /// Adjust by program base pub const R_X86_64_RELATIVE: u32 = 8; /// 32 bit signed PC relative offset to GOT pub const R_X86_64_GOTPCREL: u32 = 9; /// Direct 32 bit zero extended pub const R_X86_64_32: u32 = 10; /// Direct 32 bit sign extended pub const R_X86_64_32S: u32 = 11; /// Direct 16 bit zero extended pub const R_X86_64_16: u32 = 12; /// 16 bit sign extended pc relative pub const R_X86_64_PC16: u32 = 13; /// Direct 8 bit sign extended pub const R_X86_64_8: u32 = 14; /// 8 bit sign extended pc relative pub const R_X86_64_PC8: u32 = 15; /// ID of module containing symbol pub const R_X86_64_DTPMOD64: u32 = 16; /// Offset in module's TLS block pub const R_X86_64_DTPOFF64: u32 = 17; /// Offset in initial TLS block pub const R_X86_64_TPOFF64: u32 = 18; /// 32 bit signed PC relative offset to two GOT entries for GD symbol pub const R_X86_64_TLSGD: u32 = 19; /// 32 bit signed PC relative offset to two GOT entries for LD symbol pub const R_X86_64_TLSLD: u32 = 20; /// Offset in TLS block pub const R_X86_64_DTPOFF32: u32 = 21; /// 32 bit signed PC relative offset to GOT entry for IE symbol pub const R_X86_64_GOTTPOFF: u32 = 22; /// Offset in initial TLS block pub const R_X86_64_TPOFF32: u32 = 23; /// PC relative 64 bit pub const R_X86_64_PC64: u32 = 24; /// 64 bit offset to GOT pub const R_X86_64_GOTOFF64: u32 = 25; /// 32 bit signed pc relative offset to GOT pub const R_X86_64_GOTPC32: u32 = 26; /// 64-bit GOT entry offset pub const R_X86_64_GOT64: u32 = 27; /// 64-bit PC relative offset to GOT entry pub const R_X86_64_GOTPCREL64: u32 = 28; /// 64-bit PC relative offset to GOT pub const R_X86_64_GOTPC64: u32 = 29; /// like GOT64, says PLT entry needed pub const R_X86_64_GOTPLT64: u32 = 30; /// 64-bit GOT relative offset to PLT entry pub const R_X86_64_PLTOFF64: u32 = 31; /// Size of symbol plus 32-bit addend pub const R_X86_64_SIZE32: u32 = 32; /// Size of symbol plus 64-bit addend pub const R_X86_64_SIZE64: u32 = 33; /// GOT offset for TLS descriptor. pub const R_X86_64_GOTPC32_TLSDESC: u32 = 34; /// Marker for call through TLS descriptor. pub const R_X86_64_TLSDESC_CALL: u32 = 35; /// TLS descriptor. pub const R_X86_64_TLSDESC: u32 = 36; /// Adjust indirectly by program base pub const R_X86_64_IRELATIVE: u32 = 37; /// 64-bit adjust by program base pub const R_X86_64_RELATIVE64: u32 = 38; // 39 Reserved was R_X86_64_PC32_BND // 40 Reserved was R_X86_64_PLT32_BND /// Load from 32 bit signed pc relative offset to GOT entry without REX prefix, relaxable. pub const R_X86_64_GOTPCRELX: u32 = 41; /// Load from 32 bit signed pc relative offset to GOT entry with REX prefix, relaxable. pub const R_X86_64_REX_GOTPCRELX: u32 = 42; // AMD x86-64 values `SectionHeader*::sh_type`. /// Unwind information. pub const SHT_X86_64_UNWIND: u32 = 0x7000_0001; // AM33 values `Rel*::r_type`. /// No reloc. pub const R_MN10300_NONE: u32 = 0; /// Direct 32 bit. pub const R_MN10300_32: u32 = 1; /// Direct 16 bit. pub const R_MN10300_16: u32 = 2; /// Direct 8 bit. pub const R_MN10300_8: u32 = 3; /// PC-relative 32-bit. pub const R_MN10300_PCREL32: u32 = 4; /// PC-relative 16-bit signed. pub const R_MN10300_PCREL16: u32 = 5; /// PC-relative 8-bit signed. pub const R_MN10300_PCREL8: u32 = 6; /// Ancient C++ vtable garbage... pub const R_MN10300_GNU_VTINHERIT: u32 = 7; /// ... collection annotation. pub const R_MN10300_GNU_VTENTRY: u32 = 8; /// Direct 24 bit. pub const R_MN10300_24: u32 = 9; /// 32-bit PCrel offset to GOT. pub const R_MN10300_GOTPC32: u32 = 10; /// 16-bit PCrel offset to GOT. pub const R_MN10300_GOTPC16: u32 = 11; /// 32-bit offset from GOT. pub const R_MN10300_GOTOFF32: u32 = 12; /// 24-bit offset from GOT. pub const R_MN10300_GOTOFF24: u32 = 13; /// 16-bit offset from GOT. pub const R_MN10300_GOTOFF16: u32 = 14; /// 32-bit PCrel to PLT entry. pub const R_MN10300_PLT32: u32 = 15; /// 16-bit PCrel to PLT entry. pub const R_MN10300_PLT16: u32 = 16; /// 32-bit offset to GOT entry. pub const R_MN10300_GOT32: u32 = 17; /// 24-bit offset to GOT entry. pub const R_MN10300_GOT24: u32 = 18; /// 16-bit offset to GOT entry. pub const R_MN10300_GOT16: u32 = 19; /// Copy symbol at runtime. pub const R_MN10300_COPY: u32 = 20; /// Create GOT entry. pub const R_MN10300_GLOB_DAT: u32 = 21; /// Create PLT entry. pub const R_MN10300_JMP_SLOT: u32 = 22; /// Adjust by program base. pub const R_MN10300_RELATIVE: u32 = 23; /// 32-bit offset for global dynamic. pub const R_MN10300_TLS_GD: u32 = 24; /// 32-bit offset for local dynamic. pub const R_MN10300_TLS_LD: u32 = 25; /// Module-relative offset. pub const R_MN10300_TLS_LDO: u32 = 26; /// GOT offset for static TLS block offset. pub const R_MN10300_TLS_GOTIE: u32 = 27; /// GOT address for static TLS block offset. pub const R_MN10300_TLS_IE: u32 = 28; /// Offset relative to static TLS block. pub const R_MN10300_TLS_LE: u32 = 29; /// ID of module containing symbol. pub const R_MN10300_TLS_DTPMOD: u32 = 30; /// Offset in module TLS block. pub const R_MN10300_TLS_DTPOFF: u32 = 31; /// Offset in static TLS block. pub const R_MN10300_TLS_TPOFF: u32 = 32; /// Adjustment for next reloc as needed by linker relaxation. pub const R_MN10300_SYM_DIFF: u32 = 33; /// Alignment requirement for linker relaxation. pub const R_MN10300_ALIGN: u32 = 34; // M32R values `Rel32::r_type`. /// No reloc. pub const R_M32R_NONE: u32 = 0; /// Direct 16 bit. pub const R_M32R_16: u32 = 1; /// Direct 32 bit. pub const R_M32R_32: u32 = 2; /// Direct 24 bit. pub const R_M32R_24: u32 = 3; /// PC relative 10 bit shifted. pub const R_M32R_10_PCREL: u32 = 4; /// PC relative 18 bit shifted. pub const R_M32R_18_PCREL: u32 = 5; /// PC relative 26 bit shifted. pub const R_M32R_26_PCREL: u32 = 6; /// High 16 bit with unsigned low. pub const R_M32R_HI16_ULO: u32 = 7; /// High 16 bit with signed low. pub const R_M32R_HI16_SLO: u32 = 8; /// Low 16 bit. pub const R_M32R_LO16: u32 = 9; /// 16 bit offset in SDA. pub const R_M32R_SDA16: u32 = 10; pub const R_M32R_GNU_VTINHERIT: u32 = 11; pub const R_M32R_GNU_VTENTRY: u32 = 12; // M32R values `Rela32::r_type`. /// Direct 16 bit. pub const R_M32R_16_RELA: u32 = 33; /// Direct 32 bit. pub const R_M32R_32_RELA: u32 = 34; /// Direct 24 bit. pub const R_M32R_24_RELA: u32 = 35; /// PC relative 10 bit shifted. pub const R_M32R_10_PCREL_RELA: u32 = 36; /// PC relative 18 bit shifted. pub const R_M32R_18_PCREL_RELA: u32 = 37; /// PC relative 26 bit shifted. pub const R_M32R_26_PCREL_RELA: u32 = 38; /// High 16 bit with unsigned low pub const R_M32R_HI16_ULO_RELA: u32 = 39; /// High 16 bit with signed low pub const R_M32R_HI16_SLO_RELA: u32 = 40; /// Low 16 bit pub const R_M32R_LO16_RELA: u32 = 41; /// 16 bit offset in SDA pub const R_M32R_SDA16_RELA: u32 = 42; pub const R_M32R_RELA_GNU_VTINHERIT: u32 = 43; pub const R_M32R_RELA_GNU_VTENTRY: u32 = 44; /// PC relative 32 bit. pub const R_M32R_REL32: u32 = 45; /// 24 bit GOT entry pub const R_M32R_GOT24: u32 = 48; /// 26 bit PC relative to PLT shifted pub const R_M32R_26_PLTREL: u32 = 49; /// Copy symbol at runtime pub const R_M32R_COPY: u32 = 50; /// Create GOT entry pub const R_M32R_GLOB_DAT: u32 = 51; /// Create PLT entry pub const R_M32R_JMP_SLOT: u32 = 52; /// Adjust by program base pub const R_M32R_RELATIVE: u32 = 53; /// 24 bit offset to GOT pub const R_M32R_GOTOFF: u32 = 54; /// 24 bit PC relative offset to GOT pub const R_M32R_GOTPC24: u32 = 55; /// High 16 bit GOT entry with unsigned low pub const R_M32R_GOT16_HI_ULO: u32 = 56; /// High 16 bit GOT entry with signed low pub const R_M32R_GOT16_HI_SLO: u32 = 57; /// Low 16 bit GOT entry pub const R_M32R_GOT16_LO: u32 = 58; /// High 16 bit PC relative offset to GOT with unsigned low pub const R_M32R_GOTPC_HI_ULO: u32 = 59; /// High 16 bit PC relative offset to GOT with signed low pub const R_M32R_GOTPC_HI_SLO: u32 = 60; /// Low 16 bit PC relative offset to GOT pub const R_M32R_GOTPC_LO: u32 = 61; /// High 16 bit offset to GOT with unsigned low pub const R_M32R_GOTOFF_HI_ULO: u32 = 62; /// High 16 bit offset to GOT with signed low pub const R_M32R_GOTOFF_HI_SLO: u32 = 63; /// Low 16 bit offset to GOT pub const R_M32R_GOTOFF_LO: u32 = 64; /// Keep this the last entry. pub const R_M32R_NUM: u32 = 256; // MicroBlaze values `Rel*::r_type`. /// No reloc. pub const R_MICROBLAZE_NONE: u32 = 0; /// Direct 32 bit. pub const R_MICROBLAZE_32: u32 = 1; /// PC relative 32 bit. pub const R_MICROBLAZE_32_PCREL: u32 = 2; /// PC relative 64 bit. pub const R_MICROBLAZE_64_PCREL: u32 = 3; /// Low 16 bits of PCREL32. pub const R_MICROBLAZE_32_PCREL_LO: u32 = 4; /// Direct 64 bit. pub const R_MICROBLAZE_64: u32 = 5; /// Low 16 bit. pub const R_MICROBLAZE_32_LO: u32 = 6; /// Read-only small data area. pub const R_MICROBLAZE_SRO32: u32 = 7; /// Read-write small data area. pub const R_MICROBLAZE_SRW32: u32 = 8; /// No reloc. pub const R_MICROBLAZE_64_NONE: u32 = 9; /// Symbol Op Symbol relocation. pub const R_MICROBLAZE_32_SYM_OP_SYM: u32 = 10; /// GNU C++ vtable hierarchy. pub const R_MICROBLAZE_GNU_VTINHERIT: u32 = 11; /// GNU C++ vtable member usage. pub const R_MICROBLAZE_GNU_VTENTRY: u32 = 12; /// PC-relative GOT offset. pub const R_MICROBLAZE_GOTPC_64: u32 = 13; /// GOT entry offset. pub const R_MICROBLAZE_GOT_64: u32 = 14; /// PLT offset (PC-relative). pub const R_MICROBLAZE_PLT_64: u32 = 15; /// Adjust by program base. pub const R_MICROBLAZE_REL: u32 = 16; /// Create PLT entry. pub const R_MICROBLAZE_JUMP_SLOT: u32 = 17; /// Create GOT entry. pub const R_MICROBLAZE_GLOB_DAT: u32 = 18; /// 64 bit offset to GOT. pub const R_MICROBLAZE_GOTOFF_64: u32 = 19; /// 32 bit offset to GOT. pub const R_MICROBLAZE_GOTOFF_32: u32 = 20; /// Runtime copy. pub const R_MICROBLAZE_COPY: u32 = 21; /// TLS Reloc. pub const R_MICROBLAZE_TLS: u32 = 22; /// TLS General Dynamic. pub const R_MICROBLAZE_TLSGD: u32 = 23; /// TLS Local Dynamic. pub const R_MICROBLAZE_TLSLD: u32 = 24; /// TLS Module ID. pub const R_MICROBLAZE_TLSDTPMOD32: u32 = 25; /// TLS Offset Within TLS Block. pub const R_MICROBLAZE_TLSDTPREL32: u32 = 26; /// TLS Offset Within TLS Block. pub const R_MICROBLAZE_TLSDTPREL64: u32 = 27; /// TLS Offset From Thread Pointer. pub const R_MICROBLAZE_TLSGOTTPREL32: u32 = 28; /// TLS Offset From Thread Pointer. pub const R_MICROBLAZE_TLSTPREL32: u32 = 29; // Nios II values `Dyn::d_tag`. /// Address of _gp. pub const DT_NIOS2_GP: u32 = 0x7000_0002; // Nios II values `Rel*::r_type`. /// No reloc. pub const R_NIOS2_NONE: u32 = 0; /// Direct signed 16 bit. pub const R_NIOS2_S16: u32 = 1; /// Direct unsigned 16 bit. pub const R_NIOS2_U16: u32 = 2; /// PC relative 16 bit. pub const R_NIOS2_PCREL16: u32 = 3; /// Direct call. pub const R_NIOS2_CALL26: u32 = 4; /// 5 bit constant expression. pub const R_NIOS2_IMM5: u32 = 5; /// 5 bit expression, shift 22. pub const R_NIOS2_CACHE_OPX: u32 = 6; /// 6 bit constant expression. pub const R_NIOS2_IMM6: u32 = 7; /// 8 bit constant expression. pub const R_NIOS2_IMM8: u32 = 8; /// High 16 bit. pub const R_NIOS2_HI16: u32 = 9; /// Low 16 bit. pub const R_NIOS2_LO16: u32 = 10; /// High 16 bit, adjusted. pub const R_NIOS2_HIADJ16: u32 = 11; /// 32 bit symbol value + addend. pub const R_NIOS2_BFD_RELOC_32: u32 = 12; /// 16 bit symbol value + addend. pub const R_NIOS2_BFD_RELOC_16: u32 = 13; /// 8 bit symbol value + addend. pub const R_NIOS2_BFD_RELOC_8: u32 = 14; /// 16 bit GP pointer offset. pub const R_NIOS2_GPREL: u32 = 15; /// GNU C++ vtable hierarchy. pub const R_NIOS2_GNU_VTINHERIT: u32 = 16; /// GNU C++ vtable member usage. pub const R_NIOS2_GNU_VTENTRY: u32 = 17; /// Unconditional branch. pub const R_NIOS2_UJMP: u32 = 18; /// Conditional branch. pub const R_NIOS2_CJMP: u32 = 19; /// Indirect call through register. pub const R_NIOS2_CALLR: u32 = 20; /// Alignment requirement for linker relaxation. pub const R_NIOS2_ALIGN: u32 = 21; /// 16 bit GOT entry. pub const R_NIOS2_GOT16: u32 = 22; /// 16 bit GOT entry for function. pub const R_NIOS2_CALL16: u32 = 23; /// %lo of offset to GOT pointer. pub const R_NIOS2_GOTOFF_LO: u32 = 24; /// %hiadj of offset to GOT pointer. pub const R_NIOS2_GOTOFF_HA: u32 = 25; /// %lo of PC relative offset. pub const R_NIOS2_PCREL_LO: u32 = 26; /// %hiadj of PC relative offset. pub const R_NIOS2_PCREL_HA: u32 = 27; /// 16 bit GOT offset for TLS GD. pub const R_NIOS2_TLS_GD16: u32 = 28; /// 16 bit GOT offset for TLS LDM. pub const R_NIOS2_TLS_LDM16: u32 = 29; /// 16 bit module relative offset. pub const R_NIOS2_TLS_LDO16: u32 = 30; /// 16 bit GOT offset for TLS IE. pub const R_NIOS2_TLS_IE16: u32 = 31; /// 16 bit LE TP-relative offset. pub const R_NIOS2_TLS_LE16: u32 = 32; /// Module number. pub const R_NIOS2_TLS_DTPMOD: u32 = 33; /// Module-relative offset. pub const R_NIOS2_TLS_DTPREL: u32 = 34; /// TP-relative offset. pub const R_NIOS2_TLS_TPREL: u32 = 35; /// Copy symbol at runtime. pub const R_NIOS2_COPY: u32 = 36; /// Create GOT entry. pub const R_NIOS2_GLOB_DAT: u32 = 37; /// Create PLT entry. pub const R_NIOS2_JUMP_SLOT: u32 = 38; /// Adjust by program base. pub const R_NIOS2_RELATIVE: u32 = 39; /// 16 bit offset to GOT pointer. pub const R_NIOS2_GOTOFF: u32 = 40; /// Direct call in .noat section. pub const R_NIOS2_CALL26_NOAT: u32 = 41; /// %lo() of GOT entry. pub const R_NIOS2_GOT_LO: u32 = 42; /// %hiadj() of GOT entry. pub const R_NIOS2_GOT_HA: u32 = 43; /// %lo() of function GOT entry. pub const R_NIOS2_CALL_LO: u32 = 44; /// %hiadj() of function GOT entry. pub const R_NIOS2_CALL_HA: u32 = 45; // TILEPro values `Rel*::r_type`. /// No reloc pub const R_TILEPRO_NONE: u32 = 0; /// Direct 32 bit pub const R_TILEPRO_32: u32 = 1; /// Direct 16 bit pub const R_TILEPRO_16: u32 = 2; /// Direct 8 bit pub const R_TILEPRO_8: u32 = 3; /// PC relative 32 bit pub const R_TILEPRO_32_PCREL: u32 = 4; /// PC relative 16 bit pub const R_TILEPRO_16_PCREL: u32 = 5; /// PC relative 8 bit pub const R_TILEPRO_8_PCREL: u32 = 6; /// Low 16 bit pub const R_TILEPRO_LO16: u32 = 7; /// High 16 bit pub const R_TILEPRO_HI16: u32 = 8; /// High 16 bit, adjusted pub const R_TILEPRO_HA16: u32 = 9; /// Copy relocation pub const R_TILEPRO_COPY: u32 = 10; /// Create GOT entry pub const R_TILEPRO_GLOB_DAT: u32 = 11; /// Create PLT entry pub const R_TILEPRO_JMP_SLOT: u32 = 12; /// Adjust by program base pub const R_TILEPRO_RELATIVE: u32 = 13; /// X1 pipe branch offset pub const R_TILEPRO_BROFF_X1: u32 = 14; /// X1 pipe jump offset pub const R_TILEPRO_JOFFLONG_X1: u32 = 15; /// X1 pipe jump offset to PLT pub const R_TILEPRO_JOFFLONG_X1_PLT: u32 = 16; /// X0 pipe 8-bit pub const R_TILEPRO_IMM8_X0: u32 = 17; /// Y0 pipe 8-bit pub const R_TILEPRO_IMM8_Y0: u32 = 18; /// X1 pipe 8-bit pub const R_TILEPRO_IMM8_X1: u32 = 19; /// Y1 pipe 8-bit pub const R_TILEPRO_IMM8_Y1: u32 = 20; /// X1 pipe mtspr pub const R_TILEPRO_MT_IMM15_X1: u32 = 21; /// X1 pipe mfspr pub const R_TILEPRO_MF_IMM15_X1: u32 = 22; /// X0 pipe 16-bit pub const R_TILEPRO_IMM16_X0: u32 = 23; /// X1 pipe 16-bit pub const R_TILEPRO_IMM16_X1: u32 = 24; /// X0 pipe low 16-bit pub const R_TILEPRO_IMM16_X0_LO: u32 = 25; /// X1 pipe low 16-bit pub const R_TILEPRO_IMM16_X1_LO: u32 = 26; /// X0 pipe high 16-bit pub const R_TILEPRO_IMM16_X0_HI: u32 = 27; /// X1 pipe high 16-bit pub const R_TILEPRO_IMM16_X1_HI: u32 = 28; /// X0 pipe high 16-bit, adjusted pub const R_TILEPRO_IMM16_X0_HA: u32 = 29; /// X1 pipe high 16-bit, adjusted pub const R_TILEPRO_IMM16_X1_HA: u32 = 30; /// X0 pipe PC relative 16 bit pub const R_TILEPRO_IMM16_X0_PCREL: u32 = 31; /// X1 pipe PC relative 16 bit pub const R_TILEPRO_IMM16_X1_PCREL: u32 = 32; /// X0 pipe PC relative low 16 bit pub const R_TILEPRO_IMM16_X0_LO_PCREL: u32 = 33; /// X1 pipe PC relative low 16 bit pub const R_TILEPRO_IMM16_X1_LO_PCREL: u32 = 34; /// X0 pipe PC relative high 16 bit pub const R_TILEPRO_IMM16_X0_HI_PCREL: u32 = 35; /// X1 pipe PC relative high 16 bit pub const R_TILEPRO_IMM16_X1_HI_PCREL: u32 = 36; /// X0 pipe PC relative ha() 16 bit pub const R_TILEPRO_IMM16_X0_HA_PCREL: u32 = 37; /// X1 pipe PC relative ha() 16 bit pub const R_TILEPRO_IMM16_X1_HA_PCREL: u32 = 38; /// X0 pipe 16-bit GOT offset pub const R_TILEPRO_IMM16_X0_GOT: u32 = 39; /// X1 pipe 16-bit GOT offset pub const R_TILEPRO_IMM16_X1_GOT: u32 = 40; /// X0 pipe low 16-bit GOT offset pub const R_TILEPRO_IMM16_X0_GOT_LO: u32 = 41; /// X1 pipe low 16-bit GOT offset pub const R_TILEPRO_IMM16_X1_GOT_LO: u32 = 42; /// X0 pipe high 16-bit GOT offset pub const R_TILEPRO_IMM16_X0_GOT_HI: u32 = 43; /// X1 pipe high 16-bit GOT offset pub const R_TILEPRO_IMM16_X1_GOT_HI: u32 = 44; /// X0 pipe ha() 16-bit GOT offset pub const R_TILEPRO_IMM16_X0_GOT_HA: u32 = 45; /// X1 pipe ha() 16-bit GOT offset pub const R_TILEPRO_IMM16_X1_GOT_HA: u32 = 46; /// X0 pipe mm "start" pub const R_TILEPRO_MMSTART_X0: u32 = 47; /// X0 pipe mm "end" pub const R_TILEPRO_MMEND_X0: u32 = 48; /// X1 pipe mm "start" pub const R_TILEPRO_MMSTART_X1: u32 = 49; /// X1 pipe mm "end" pub const R_TILEPRO_MMEND_X1: u32 = 50; /// X0 pipe shift amount pub const R_TILEPRO_SHAMT_X0: u32 = 51; /// X1 pipe shift amount pub const R_TILEPRO_SHAMT_X1: u32 = 52; /// Y0 pipe shift amount pub const R_TILEPRO_SHAMT_Y0: u32 = 53; /// Y1 pipe shift amount pub const R_TILEPRO_SHAMT_Y1: u32 = 54; /// X1 pipe destination 8-bit pub const R_TILEPRO_DEST_IMM8_X1: u32 = 55; // Relocs 56-59 are currently not defined. /// "jal" for TLS GD pub const R_TILEPRO_TLS_GD_CALL: u32 = 60; /// X0 pipe "addi" for TLS GD pub const R_TILEPRO_IMM8_X0_TLS_GD_ADD: u32 = 61; /// X1 pipe "addi" for TLS GD pub const R_TILEPRO_IMM8_X1_TLS_GD_ADD: u32 = 62; /// Y0 pipe "addi" for TLS GD pub const R_TILEPRO_IMM8_Y0_TLS_GD_ADD: u32 = 63; /// Y1 pipe "addi" for TLS GD pub const R_TILEPRO_IMM8_Y1_TLS_GD_ADD: u32 = 64; /// "lw_tls" for TLS IE pub const R_TILEPRO_TLS_IE_LOAD: u32 = 65; /// X0 pipe 16-bit TLS GD offset pub const R_TILEPRO_IMM16_X0_TLS_GD: u32 = 66; /// X1 pipe 16-bit TLS GD offset pub const R_TILEPRO_IMM16_X1_TLS_GD: u32 = 67; /// X0 pipe low 16-bit TLS GD offset pub const R_TILEPRO_IMM16_X0_TLS_GD_LO: u32 = 68; /// X1 pipe low 16-bit TLS GD offset pub const R_TILEPRO_IMM16_X1_TLS_GD_LO: u32 = 69; /// X0 pipe high 16-bit TLS GD offset pub const R_TILEPRO_IMM16_X0_TLS_GD_HI: u32 = 70; /// X1 pipe high 16-bit TLS GD offset pub const R_TILEPRO_IMM16_X1_TLS_GD_HI: u32 = 71; /// X0 pipe ha() 16-bit TLS GD offset pub const R_TILEPRO_IMM16_X0_TLS_GD_HA: u32 = 72; /// X1 pipe ha() 16-bit TLS GD offset pub const R_TILEPRO_IMM16_X1_TLS_GD_HA: u32 = 73; /// X0 pipe 16-bit TLS IE offset pub const R_TILEPRO_IMM16_X0_TLS_IE: u32 = 74; /// X1 pipe 16-bit TLS IE offset pub const R_TILEPRO_IMM16_X1_TLS_IE: u32 = 75; /// X0 pipe low 16-bit TLS IE offset pub const R_TILEPRO_IMM16_X0_TLS_IE_LO: u32 = 76; /// X1 pipe low 16-bit TLS IE offset pub const R_TILEPRO_IMM16_X1_TLS_IE_LO: u32 = 77; /// X0 pipe high 16-bit TLS IE offset pub const R_TILEPRO_IMM16_X0_TLS_IE_HI: u32 = 78; /// X1 pipe high 16-bit TLS IE offset pub const R_TILEPRO_IMM16_X1_TLS_IE_HI: u32 = 79; /// X0 pipe ha() 16-bit TLS IE offset pub const R_TILEPRO_IMM16_X0_TLS_IE_HA: u32 = 80; /// X1 pipe ha() 16-bit TLS IE offset pub const R_TILEPRO_IMM16_X1_TLS_IE_HA: u32 = 81; /// ID of module containing symbol pub const R_TILEPRO_TLS_DTPMOD32: u32 = 82; /// Offset in TLS block pub const R_TILEPRO_TLS_DTPOFF32: u32 = 83; /// Offset in static TLS block pub const R_TILEPRO_TLS_TPOFF32: u32 = 84; /// X0 pipe 16-bit TLS LE offset pub const R_TILEPRO_IMM16_X0_TLS_LE: u32 = 85; /// X1 pipe 16-bit TLS LE offset pub const R_TILEPRO_IMM16_X1_TLS_LE: u32 = 86; /// X0 pipe low 16-bit TLS LE offset pub const R_TILEPRO_IMM16_X0_TLS_LE_LO: u32 = 87; /// X1 pipe low 16-bit TLS LE offset pub const R_TILEPRO_IMM16_X1_TLS_LE_LO: u32 = 88; /// X0 pipe high 16-bit TLS LE offset pub const R_TILEPRO_IMM16_X0_TLS_LE_HI: u32 = 89; /// X1 pipe high 16-bit TLS LE offset pub const R_TILEPRO_IMM16_X1_TLS_LE_HI: u32 = 90; /// X0 pipe ha() 16-bit TLS LE offset pub const R_TILEPRO_IMM16_X0_TLS_LE_HA: u32 = 91; /// X1 pipe ha() 16-bit TLS LE offset pub const R_TILEPRO_IMM16_X1_TLS_LE_HA: u32 = 92; /// GNU C++ vtable hierarchy pub const R_TILEPRO_GNU_VTINHERIT: u32 = 128; /// GNU C++ vtable member usage pub const R_TILEPRO_GNU_VTENTRY: u32 = 129; // TILE-Gx values `Rel*::r_type`. /// No reloc pub const R_TILEGX_NONE: u32 = 0; /// Direct 64 bit pub const R_TILEGX_64: u32 = 1; /// Direct 32 bit pub const R_TILEGX_32: u32 = 2; /// Direct 16 bit pub const R_TILEGX_16: u32 = 3; /// Direct 8 bit pub const R_TILEGX_8: u32 = 4; /// PC relative 64 bit pub const R_TILEGX_64_PCREL: u32 = 5; /// PC relative 32 bit pub const R_TILEGX_32_PCREL: u32 = 6; /// PC relative 16 bit pub const R_TILEGX_16_PCREL: u32 = 7; /// PC relative 8 bit pub const R_TILEGX_8_PCREL: u32 = 8; /// hword 0 16-bit pub const R_TILEGX_HW0: u32 = 9; /// hword 1 16-bit pub const R_TILEGX_HW1: u32 = 10; /// hword 2 16-bit pub const R_TILEGX_HW2: u32 = 11; /// hword 3 16-bit pub const R_TILEGX_HW3: u32 = 12; /// last hword 0 16-bit pub const R_TILEGX_HW0_LAST: u32 = 13; /// last hword 1 16-bit pub const R_TILEGX_HW1_LAST: u32 = 14; /// last hword 2 16-bit pub const R_TILEGX_HW2_LAST: u32 = 15; /// Copy relocation pub const R_TILEGX_COPY: u32 = 16; /// Create GOT entry pub const R_TILEGX_GLOB_DAT: u32 = 17; /// Create PLT entry pub const R_TILEGX_JMP_SLOT: u32 = 18; /// Adjust by program base pub const R_TILEGX_RELATIVE: u32 = 19; /// X1 pipe branch offset pub const R_TILEGX_BROFF_X1: u32 = 20; /// X1 pipe jump offset pub const R_TILEGX_JUMPOFF_X1: u32 = 21; /// X1 pipe jump offset to PLT pub const R_TILEGX_JUMPOFF_X1_PLT: u32 = 22; /// X0 pipe 8-bit pub const R_TILEGX_IMM8_X0: u32 = 23; /// Y0 pipe 8-bit pub const R_TILEGX_IMM8_Y0: u32 = 24; /// X1 pipe 8-bit pub const R_TILEGX_IMM8_X1: u32 = 25; /// Y1 pipe 8-bit pub const R_TILEGX_IMM8_Y1: u32 = 26; /// X1 pipe destination 8-bit pub const R_TILEGX_DEST_IMM8_X1: u32 = 27; /// X1 pipe mtspr pub const R_TILEGX_MT_IMM14_X1: u32 = 28; /// X1 pipe mfspr pub const R_TILEGX_MF_IMM14_X1: u32 = 29; /// X0 pipe mm "start" pub const R_TILEGX_MMSTART_X0: u32 = 30; /// X0 pipe mm "end" pub const R_TILEGX_MMEND_X0: u32 = 31; /// X0 pipe shift amount pub const R_TILEGX_SHAMT_X0: u32 = 32; /// X1 pipe shift amount pub const R_TILEGX_SHAMT_X1: u32 = 33; /// Y0 pipe shift amount pub const R_TILEGX_SHAMT_Y0: u32 = 34; /// Y1 pipe shift amount pub const R_TILEGX_SHAMT_Y1: u32 = 35; /// X0 pipe hword 0 pub const R_TILEGX_IMM16_X0_HW0: u32 = 36; /// X1 pipe hword 0 pub const R_TILEGX_IMM16_X1_HW0: u32 = 37; /// X0 pipe hword 1 pub const R_TILEGX_IMM16_X0_HW1: u32 = 38; /// X1 pipe hword 1 pub const R_TILEGX_IMM16_X1_HW1: u32 = 39; /// X0 pipe hword 2 pub const R_TILEGX_IMM16_X0_HW2: u32 = 40; /// X1 pipe hword 2 pub const R_TILEGX_IMM16_X1_HW2: u32 = 41; /// X0 pipe hword 3 pub const R_TILEGX_IMM16_X0_HW3: u32 = 42; /// X1 pipe hword 3 pub const R_TILEGX_IMM16_X1_HW3: u32 = 43; /// X0 pipe last hword 0 pub const R_TILEGX_IMM16_X0_HW0_LAST: u32 = 44; /// X1 pipe last hword 0 pub const R_TILEGX_IMM16_X1_HW0_LAST: u32 = 45; /// X0 pipe last hword 1 pub const R_TILEGX_IMM16_X0_HW1_LAST: u32 = 46; /// X1 pipe last hword 1 pub const R_TILEGX_IMM16_X1_HW1_LAST: u32 = 47; /// X0 pipe last hword 2 pub const R_TILEGX_IMM16_X0_HW2_LAST: u32 = 48; /// X1 pipe last hword 2 pub const R_TILEGX_IMM16_X1_HW2_LAST: u32 = 49; /// X0 pipe PC relative hword 0 pub const R_TILEGX_IMM16_X0_HW0_PCREL: u32 = 50; /// X1 pipe PC relative hword 0 pub const R_TILEGX_IMM16_X1_HW0_PCREL: u32 = 51; /// X0 pipe PC relative hword 1 pub const R_TILEGX_IMM16_X0_HW1_PCREL: u32 = 52; /// X1 pipe PC relative hword 1 pub const R_TILEGX_IMM16_X1_HW1_PCREL: u32 = 53; /// X0 pipe PC relative hword 2 pub const R_TILEGX_IMM16_X0_HW2_PCREL: u32 = 54; /// X1 pipe PC relative hword 2 pub const R_TILEGX_IMM16_X1_HW2_PCREL: u32 = 55; /// X0 pipe PC relative hword 3 pub const R_TILEGX_IMM16_X0_HW3_PCREL: u32 = 56; /// X1 pipe PC relative hword 3 pub const R_TILEGX_IMM16_X1_HW3_PCREL: u32 = 57; /// X0 pipe PC-rel last hword 0 pub const R_TILEGX_IMM16_X0_HW0_LAST_PCREL: u32 = 58; /// X1 pipe PC-rel last hword 0 pub const R_TILEGX_IMM16_X1_HW0_LAST_PCREL: u32 = 59; /// X0 pipe PC-rel last hword 1 pub const R_TILEGX_IMM16_X0_HW1_LAST_PCREL: u32 = 60; /// X1 pipe PC-rel last hword 1 pub const R_TILEGX_IMM16_X1_HW1_LAST_PCREL: u32 = 61; /// X0 pipe PC-rel last hword 2 pub const R_TILEGX_IMM16_X0_HW2_LAST_PCREL: u32 = 62; /// X1 pipe PC-rel last hword 2 pub const R_TILEGX_IMM16_X1_HW2_LAST_PCREL: u32 = 63; /// X0 pipe hword 0 GOT offset pub const R_TILEGX_IMM16_X0_HW0_GOT: u32 = 64; /// X1 pipe hword 0 GOT offset pub const R_TILEGX_IMM16_X1_HW0_GOT: u32 = 65; /// X0 pipe PC-rel PLT hword 0 pub const R_TILEGX_IMM16_X0_HW0_PLT_PCREL: u32 = 66; /// X1 pipe PC-rel PLT hword 0 pub const R_TILEGX_IMM16_X1_HW0_PLT_PCREL: u32 = 67; /// X0 pipe PC-rel PLT hword 1 pub const R_TILEGX_IMM16_X0_HW1_PLT_PCREL: u32 = 68; /// X1 pipe PC-rel PLT hword 1 pub const R_TILEGX_IMM16_X1_HW1_PLT_PCREL: u32 = 69; /// X0 pipe PC-rel PLT hword 2 pub const R_TILEGX_IMM16_X0_HW2_PLT_PCREL: u32 = 70; /// X1 pipe PC-rel PLT hword 2 pub const R_TILEGX_IMM16_X1_HW2_PLT_PCREL: u32 = 71; /// X0 pipe last hword 0 GOT offset pub const R_TILEGX_IMM16_X0_HW0_LAST_GOT: u32 = 72; /// X1 pipe last hword 0 GOT offset pub const R_TILEGX_IMM16_X1_HW0_LAST_GOT: u32 = 73; /// X0 pipe last hword 1 GOT offset pub const R_TILEGX_IMM16_X0_HW1_LAST_GOT: u32 = 74; /// X1 pipe last hword 1 GOT offset pub const R_TILEGX_IMM16_X1_HW1_LAST_GOT: u32 = 75; /// X0 pipe PC-rel PLT hword 3 pub const R_TILEGX_IMM16_X0_HW3_PLT_PCREL: u32 = 76; /// X1 pipe PC-rel PLT hword 3 pub const R_TILEGX_IMM16_X1_HW3_PLT_PCREL: u32 = 77; /// X0 pipe hword 0 TLS GD offset pub const R_TILEGX_IMM16_X0_HW0_TLS_GD: u32 = 78; /// X1 pipe hword 0 TLS GD offset pub const R_TILEGX_IMM16_X1_HW0_TLS_GD: u32 = 79; /// X0 pipe hword 0 TLS LE offset pub const R_TILEGX_IMM16_X0_HW0_TLS_LE: u32 = 80; /// X1 pipe hword 0 TLS LE offset pub const R_TILEGX_IMM16_X1_HW0_TLS_LE: u32 = 81; /// X0 pipe last hword 0 LE off pub const R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE: u32 = 82; /// X1 pipe last hword 0 LE off pub const R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE: u32 = 83; /// X0 pipe last hword 1 LE off pub const R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE: u32 = 84; /// X1 pipe last hword 1 LE off pub const R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE: u32 = 85; /// X0 pipe last hword 0 GD off pub const R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD: u32 = 86; /// X1 pipe last hword 0 GD off pub const R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD: u32 = 87; /// X0 pipe last hword 1 GD off pub const R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD: u32 = 88; /// X1 pipe last hword 1 GD off pub const R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD: u32 = 89; // Relocs 90-91 are currently not defined. /// X0 pipe hword 0 TLS IE offset pub const R_TILEGX_IMM16_X0_HW0_TLS_IE: u32 = 92; /// X1 pipe hword 0 TLS IE offset pub const R_TILEGX_IMM16_X1_HW0_TLS_IE: u32 = 93; /// X0 pipe PC-rel PLT last hword 0 pub const R_TILEGX_IMM16_X0_HW0_LAST_PLT_PCREL: u32 = 94; /// X1 pipe PC-rel PLT last hword 0 pub const R_TILEGX_IMM16_X1_HW0_LAST_PLT_PCREL: u32 = 95; /// X0 pipe PC-rel PLT last hword 1 pub const R_TILEGX_IMM16_X0_HW1_LAST_PLT_PCREL: u32 = 96; /// X1 pipe PC-rel PLT last hword 1 pub const R_TILEGX_IMM16_X1_HW1_LAST_PLT_PCREL: u32 = 97; /// X0 pipe PC-rel PLT last hword 2 pub const R_TILEGX_IMM16_X0_HW2_LAST_PLT_PCREL: u32 = 98; /// X1 pipe PC-rel PLT last hword 2 pub const R_TILEGX_IMM16_X1_HW2_LAST_PLT_PCREL: u32 = 99; /// X0 pipe last hword 0 IE off pub const R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE: u32 = 100; /// X1 pipe last hword 0 IE off pub const R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE: u32 = 101; /// X0 pipe last hword 1 IE off pub const R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE: u32 = 102; /// X1 pipe last hword 1 IE off pub const R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE: u32 = 103; // Relocs 104-105 are currently not defined. /// 64-bit ID of symbol's module pub const R_TILEGX_TLS_DTPMOD64: u32 = 106; /// 64-bit offset in TLS block pub const R_TILEGX_TLS_DTPOFF64: u32 = 107; /// 64-bit offset in static TLS block pub const R_TILEGX_TLS_TPOFF64: u32 = 108; /// 32-bit ID of symbol's module pub const R_TILEGX_TLS_DTPMOD32: u32 = 109; /// 32-bit offset in TLS block pub const R_TILEGX_TLS_DTPOFF32: u32 = 110; /// 32-bit offset in static TLS block pub const R_TILEGX_TLS_TPOFF32: u32 = 111; /// "jal" for TLS GD pub const R_TILEGX_TLS_GD_CALL: u32 = 112; /// X0 pipe "addi" for TLS GD pub const R_TILEGX_IMM8_X0_TLS_GD_ADD: u32 = 113; /// X1 pipe "addi" for TLS GD pub const R_TILEGX_IMM8_X1_TLS_GD_ADD: u32 = 114; /// Y0 pipe "addi" for TLS GD pub const R_TILEGX_IMM8_Y0_TLS_GD_ADD: u32 = 115; /// Y1 pipe "addi" for TLS GD pub const R_TILEGX_IMM8_Y1_TLS_GD_ADD: u32 = 116; /// "ld_tls" for TLS IE pub const R_TILEGX_TLS_IE_LOAD: u32 = 117; /// X0 pipe "addi" for TLS GD/IE pub const R_TILEGX_IMM8_X0_TLS_ADD: u32 = 118; /// X1 pipe "addi" for TLS GD/IE pub const R_TILEGX_IMM8_X1_TLS_ADD: u32 = 119; /// Y0 pipe "addi" for TLS GD/IE pub const R_TILEGX_IMM8_Y0_TLS_ADD: u32 = 120; /// Y1 pipe "addi" for TLS GD/IE pub const R_TILEGX_IMM8_Y1_TLS_ADD: u32 = 121; /// GNU C++ vtable hierarchy pub const R_TILEGX_GNU_VTINHERIT: u32 = 128; /// GNU C++ vtable member usage pub const R_TILEGX_GNU_VTENTRY: u32 = 129; // RISC-V values `FileHeader*::e_flags`. pub const EF_RISCV_RVC: u32 = 0x0001; pub const EF_RISCV_FLOAT_ABI: u32 = 0x0006; pub const EF_RISCV_FLOAT_ABI_SOFT: u32 = 0x0000; pub const EF_RISCV_FLOAT_ABI_SINGLE: u32 = 0x0002; pub const EF_RISCV_FLOAT_ABI_DOUBLE: u32 = 0x0004; pub const EF_RISCV_FLOAT_ABI_QUAD: u32 = 0x0006; pub const EF_RISCV_RVE: u32 = 0x0008; pub const EF_RISCV_TSO: u32 = 0x0010; // RISC-V values for `SectionHeader*::sh_type`. /// RISC-V attributes section. pub const SHT_RISCV_ATTRIBUTES: u32 = SHT_LOPROC + 3; // RISC-V values `Rel*::r_type`. pub const R_RISCV_NONE: u32 = 0; pub const R_RISCV_32: u32 = 1; pub const R_RISCV_64: u32 = 2; pub const R_RISCV_RELATIVE: u32 = 3; pub const R_RISCV_COPY: u32 = 4; pub const R_RISCV_JUMP_SLOT: u32 = 5; pub const R_RISCV_TLS_DTPMOD32: u32 = 6; pub const R_RISCV_TLS_DTPMOD64: u32 = 7; pub const R_RISCV_TLS_DTPREL32: u32 = 8; pub const R_RISCV_TLS_DTPREL64: u32 = 9; pub const R_RISCV_TLS_TPREL32: u32 = 10; pub const R_RISCV_TLS_TPREL64: u32 = 11; pub const R_RISCV_BRANCH: u32 = 16; pub const R_RISCV_JAL: u32 = 17; pub const R_RISCV_CALL: u32 = 18; pub const R_RISCV_CALL_PLT: u32 = 19; pub const R_RISCV_GOT_HI20: u32 = 20; pub const R_RISCV_TLS_GOT_HI20: u32 = 21; pub const R_RISCV_TLS_GD_HI20: u32 = 22; pub const R_RISCV_PCREL_HI20: u32 = 23; pub const R_RISCV_PCREL_LO12_I: u32 = 24; pub const R_RISCV_PCREL_LO12_S: u32 = 25; pub const R_RISCV_HI20: u32 = 26; pub const R_RISCV_LO12_I: u32 = 27; pub const R_RISCV_LO12_S: u32 = 28; pub const R_RISCV_TPREL_HI20: u32 = 29; pub const R_RISCV_TPREL_LO12_I: u32 = 30; pub const R_RISCV_TPREL_LO12_S: u32 = 31; pub const R_RISCV_TPREL_ADD: u32 = 32; pub const R_RISCV_ADD8: u32 = 33; pub const R_RISCV_ADD16: u32 = 34; pub const R_RISCV_ADD32: u32 = 35; pub const R_RISCV_ADD64: u32 = 36; pub const R_RISCV_SUB8: u32 = 37; pub const R_RISCV_SUB16: u32 = 38; pub const R_RISCV_SUB32: u32 = 39; pub const R_RISCV_SUB64: u32 = 40; pub const R_RISCV_GNU_VTINHERIT: u32 = 41; pub const R_RISCV_GNU_VTENTRY: u32 = 42; pub const R_RISCV_ALIGN: u32 = 43; pub const R_RISCV_RVC_BRANCH: u32 = 44; pub const R_RISCV_RVC_JUMP: u32 = 45; pub const R_RISCV_RVC_LUI: u32 = 46; pub const R_RISCV_GPREL_I: u32 = 47; pub const R_RISCV_GPREL_S: u32 = 48; pub const R_RISCV_TPREL_I: u32 = 49; pub const R_RISCV_TPREL_S: u32 = 50; pub const R_RISCV_RELAX: u32 = 51; pub const R_RISCV_SUB6: u32 = 52; pub const R_RISCV_SET6: u32 = 53; pub const R_RISCV_SET8: u32 = 54; pub const R_RISCV_SET16: u32 = 55; pub const R_RISCV_SET32: u32 = 56; pub const R_RISCV_32_PCREL: u32 = 57; pub const R_RISCV_IRELATIVE: u32 = 58; pub const R_RISCV_PLT32: u32 = 59; pub const R_RISCV_SET_ULEB128: u32 = 60; pub const R_RISCV_SUB_ULEB128: u32 = 61; pub const R_RISCV_TLSDESC_HI20: u32 = 62; pub const R_RISCV_TLSDESC_LOAD_LO12: u32 = 63; pub const R_RISCV_TLSDESC_ADD_LO12: u32 = 64; pub const R_RISCV_TLSDESC_CALL: u32 = 65; // BPF values `Rel*::r_type`. /// No reloc pub const R_BPF_NONE: u32 = 0; pub const R_BPF_64_64: u32 = 1; pub const R_BPF_64_32: u32 = 10; // SBF values `Rel*::r_type`. /// No reloc pub const R_SBF_NONE: u32 = 0; pub const R_SBF_64_64: u32 = 1; pub const R_SBF_64_32: u32 = 10; // Imagination Meta values `Rel*::r_type`. pub const R_METAG_HIADDR16: u32 = 0; pub const R_METAG_LOADDR16: u32 = 1; /// 32bit absolute address pub const R_METAG_ADDR32: u32 = 2; /// No reloc pub const R_METAG_NONE: u32 = 3; pub const R_METAG_RELBRANCH: u32 = 4; pub const R_METAG_GETSETOFF: u32 = 5; // Backward compatibility pub const R_METAG_REG32OP1: u32 = 6; pub const R_METAG_REG32OP2: u32 = 7; pub const R_METAG_REG32OP3: u32 = 8; pub const R_METAG_REG16OP1: u32 = 9; pub const R_METAG_REG16OP2: u32 = 10; pub const R_METAG_REG16OP3: u32 = 11; pub const R_METAG_REG32OP4: u32 = 12; pub const R_METAG_HIOG: u32 = 13; pub const R_METAG_LOOG: u32 = 14; pub const R_METAG_REL8: u32 = 15; pub const R_METAG_REL16: u32 = 16; pub const R_METAG_GNU_VTINHERIT: u32 = 30; pub const R_METAG_GNU_VTENTRY: u32 = 31; // PIC relocations pub const R_METAG_HI16_GOTOFF: u32 = 32; pub const R_METAG_LO16_GOTOFF: u32 = 33; pub const R_METAG_GETSET_GOTOFF: u32 = 34; pub const R_METAG_GETSET_GOT: u32 = 35; pub const R_METAG_HI16_GOTPC: u32 = 36; pub const R_METAG_LO16_GOTPC: u32 = 37; pub const R_METAG_HI16_PLT: u32 = 38; pub const R_METAG_LO16_PLT: u32 = 39; pub const R_METAG_RELBRANCH_PLT: u32 = 40; pub const R_METAG_GOTOFF: u32 = 41; pub const R_METAG_PLT: u32 = 42; pub const R_METAG_COPY: u32 = 43; pub const R_METAG_JMP_SLOT: u32 = 44; pub const R_METAG_RELATIVE: u32 = 45; pub const R_METAG_GLOB_DAT: u32 = 46; // TLS relocations pub const R_METAG_TLS_GD: u32 = 47; pub const R_METAG_TLS_LDM: u32 = 48; pub const R_METAG_TLS_LDO_HI16: u32 = 49; pub const R_METAG_TLS_LDO_LO16: u32 = 50; pub const R_METAG_TLS_LDO: u32 = 51; pub const R_METAG_TLS_IE: u32 = 52; pub const R_METAG_TLS_IENONPIC: u32 = 53; pub const R_METAG_TLS_IENONPIC_HI16: u32 = 54; pub const R_METAG_TLS_IENONPIC_LO16: u32 = 55; pub const R_METAG_TLS_TPOFF: u32 = 56; pub const R_METAG_TLS_DTPMOD: u32 = 57; pub const R_METAG_TLS_DTPOFF: u32 = 58; pub const R_METAG_TLS_LE: u32 = 59; pub const R_METAG_TLS_LE_HI16: u32 = 60; pub const R_METAG_TLS_LE_LO16: u32 = 61; // NDS32 values `Rel*::r_type`. pub const R_NDS32_NONE: u32 = 0; pub const R_NDS32_32_RELA: u32 = 20; pub const R_NDS32_COPY: u32 = 39; pub const R_NDS32_GLOB_DAT: u32 = 40; pub const R_NDS32_JMP_SLOT: u32 = 41; pub const R_NDS32_RELATIVE: u32 = 42; pub const R_NDS32_TLS_TPOFF: u32 = 102; pub const R_NDS32_TLS_DESC: u32 = 119; // LoongArch values `FileHeader*::e_flags`. /// Additional properties of the base ABI type, including the FP calling /// convention. pub const EF_LARCH_ABI_MODIFIER_MASK: u32 = 0x7; /// Uses GPRs and the stack for parameter passing pub const EF_LARCH_ABI_SOFT_FLOAT: u32 = 0x1; /// Uses GPRs, 32-bit FPRs and the stack for parameter passing pub const EF_LARCH_ABI_SINGLE_FLOAT: u32 = 0x2; /// Uses GPRs, 64-bit FPRs and the stack for parameter passing pub const EF_LARCH_ABI_DOUBLE_FLOAT: u32 = 0x3; /// Uses relocation types directly writing to immediate slots pub const EF_LARCH_OBJABI_V1: u32 = 0x40; // LoongArch values `Rel*::r_type`. /// No reloc pub const R_LARCH_NONE: u32 = 0; /// Runtime address resolving pub const R_LARCH_32: u32 = 1; /// Runtime address resolving pub const R_LARCH_64: u32 = 2; /// Runtime fixup for load-address pub const R_LARCH_RELATIVE: u32 = 3; /// Runtime memory copy in executable pub const R_LARCH_COPY: u32 = 4; /// Runtime PLT supporting pub const R_LARCH_JUMP_SLOT: u32 = 5; /// Runtime relocation for TLS-GD pub const R_LARCH_TLS_DTPMOD32: u32 = 6; /// Runtime relocation for TLS-GD pub const R_LARCH_TLS_DTPMOD64: u32 = 7; /// Runtime relocation for TLS-GD pub const R_LARCH_TLS_DTPREL32: u32 = 8; /// Runtime relocation for TLS-GD pub const R_LARCH_TLS_DTPREL64: u32 = 9; /// Runtime relocation for TLE-IE pub const R_LARCH_TLS_TPREL32: u32 = 10; /// Runtime relocation for TLE-IE pub const R_LARCH_TLS_TPREL64: u32 = 11; /// Runtime local indirect function resolving pub const R_LARCH_IRELATIVE: u32 = 12; /// Mark la.abs: load absolute address for static link. pub const R_LARCH_MARK_LA: u32 = 20; /// Mark external label branch: access PC relative address for static link. pub const R_LARCH_MARK_PCREL: u32 = 21; /// Push PC-relative offset pub const R_LARCH_SOP_PUSH_PCREL: u32 = 22; /// Push constant or absolute address pub const R_LARCH_SOP_PUSH_ABSOLUTE: u32 = 23; /// Duplicate stack top pub const R_LARCH_SOP_PUSH_DUP: u32 = 24; /// Push for access GOT entry pub const R_LARCH_SOP_PUSH_GPREL: u32 = 25; /// Push for TLS-LE pub const R_LARCH_SOP_PUSH_TLS_TPREL: u32 = 26; /// Push for TLS-IE pub const R_LARCH_SOP_PUSH_TLS_GOT: u32 = 27; /// Push for TLS-GD pub const R_LARCH_SOP_PUSH_TLS_GD: u32 = 28; /// Push for external function calling pub const R_LARCH_SOP_PUSH_PLT_PCREL: u32 = 29; /// Assert stack top pub const R_LARCH_SOP_ASSERT: u32 = 30; /// Stack top logical not (unary) pub const R_LARCH_SOP_NOT: u32 = 31; /// Stack top subtraction (binary) pub const R_LARCH_SOP_SUB: u32 = 32; /// Stack top left shift (binary) pub const R_LARCH_SOP_SL: u32 = 33; /// Stack top right shift (binary) pub const R_LARCH_SOP_SR: u32 = 34; /// Stack top addition (binary) pub const R_LARCH_SOP_ADD: u32 = 35; /// Stack top bitwise and (binary) pub const R_LARCH_SOP_AND: u32 = 36; /// Stack top selection (tertiary) pub const R_LARCH_SOP_IF_ELSE: u32 = 37; /// Pop stack top to fill 5-bit signed immediate operand pub const R_LARCH_SOP_POP_32_S_10_5: u32 = 38; /// Pop stack top to fill 12-bit unsigned immediate operand pub const R_LARCH_SOP_POP_32_U_10_12: u32 = 39; /// Pop stack top to fill 12-bit signed immediate operand pub const R_LARCH_SOP_POP_32_S_10_12: u32 = 40; /// Pop stack top to fill 16-bit signed immediate operand pub const R_LARCH_SOP_POP_32_S_10_16: u32 = 41; /// Pop stack top to fill 18-bit signed immediate operand with two trailing /// zeros implied pub const R_LARCH_SOP_POP_32_S_10_16_S2: u32 = 42; /// Pop stack top to fill 20-bit signed immediate operand pub const R_LARCH_SOP_POP_32_S_5_20: u32 = 43; /// Pop stack top to fill 23-bit signed immediate operand with two trailing /// zeros implied pub const R_LARCH_SOP_POP_32_S_0_5_10_16_S2: u32 = 44; /// Pop stack top to fill 28-bit signed immediate operand with two trailing /// zeros implied pub const R_LARCH_SOP_POP_32_S_0_10_10_16_S2: u32 = 45; /// Pop stack top to fill an instruction pub const R_LARCH_SOP_POP_32_U: u32 = 46; /// 8-bit in-place addition pub const R_LARCH_ADD8: u32 = 47; /// 16-bit in-place addition pub const R_LARCH_ADD16: u32 = 48; /// 24-bit in-place addition pub const R_LARCH_ADD24: u32 = 49; /// 32-bit in-place addition pub const R_LARCH_ADD32: u32 = 50; /// 64-bit in-place addition pub const R_LARCH_ADD64: u32 = 51; /// 8-bit in-place subtraction pub const R_LARCH_SUB8: u32 = 52; /// 16-bit in-place subtraction pub const R_LARCH_SUB16: u32 = 53; /// 24-bit in-place subtraction pub const R_LARCH_SUB24: u32 = 54; /// 32-bit in-place subtraction pub const R_LARCH_SUB32: u32 = 55; /// 64-bit in-place subtraction pub const R_LARCH_SUB64: u32 = 56; /// GNU C++ vtable hierarchy pub const R_LARCH_GNU_VTINHERIT: u32 = 57; /// GNU C++ vtable member usage pub const R_LARCH_GNU_VTENTRY: u32 = 58; /// 18-bit PC-relative jump offset with two trailing zeros pub const R_LARCH_B16: u32 = 64; /// 23-bit PC-relative jump offset with two trailing zeros pub const R_LARCH_B21: u32 = 65; /// 28-bit PC-relative jump offset with two trailing zeros pub const R_LARCH_B26: u32 = 66; /// 12..=31 bits of 32/64-bit absolute address pub const R_LARCH_ABS_HI20: u32 = 67; /// 0..=11 bits of 32/64-bit absolute address pub const R_LARCH_ABS_LO12: u32 = 68; /// 32..=51 bits of 64-bit absolute address pub const R_LARCH_ABS64_LO20: u32 = 69; /// 52..=63 bits of 64-bit absolute address pub const R_LARCH_ABS64_HI12: u32 = 70; /// The signed 32-bit offset `offs` from `PC & 0xfffff000` to /// `(S + A + 0x800) & 0xfffff000`, with 12 trailing zeros removed. /// /// We define the *PC relative anchor* for `S + A` as `PC + offs` (`offs` /// is sign-extended to VA bits). pub const R_LARCH_PCALA_HI20: u32 = 71; /// Same as R_LARCH_ABS_LO12. 0..=11 bits of the 32/64-bit offset from the /// [PC relative anchor][R_LARCH_PCALA_HI20]. pub const R_LARCH_PCALA_LO12: u32 = 72; /// 32..=51 bits of the 64-bit offset from the /// [PC relative anchor][R_LARCH_PCALA_HI20]. pub const R_LARCH_PCALA64_LO20: u32 = 73; /// 52..=63 bits of the 64-bit offset from the /// [PC relative anchor][R_LARCH_PCALA_HI20]. pub const R_LARCH_PCALA64_HI12: u32 = 74; /// The signed 32-bit offset `offs` from `PC & 0xfffff000` to /// `(GP + G + 0x800) & 0xfffff000`, with 12 trailing zeros removed. /// /// We define the *PC relative anchor* for the GOT entry at `GP + G` as /// `PC + offs` (`offs` is sign-extended to VA bits). pub const R_LARCH_GOT_PC_HI20: u32 = 75; /// 0..=11 bits of the 32/64-bit offset from the /// [PC relative anchor][R_LARCH_GOT_PC_HI20] to the GOT entry. pub const R_LARCH_GOT_PC_LO12: u32 = 76; /// 32..=51 bits of the 64-bit offset from the /// [PC relative anchor][R_LARCH_GOT_PC_HI20] to the GOT entry. pub const R_LARCH_GOT64_PC_LO20: u32 = 77; /// 52..=63 bits of the 64-bit offset from the /// [PC relative anchor][R_LARCH_GOT_PC_HI20] to the GOT entry. pub const R_LARCH_GOT64_PC_HI12: u32 = 78; /// 12..=31 bits of 32/64-bit GOT entry absolute address pub const R_LARCH_GOT_HI20: u32 = 79; /// 0..=11 bits of 32/64-bit GOT entry absolute address pub const R_LARCH_GOT_LO12: u32 = 80; /// 32..=51 bits of 64-bit GOT entry absolute address pub const R_LARCH_GOT64_LO20: u32 = 81; /// 52..=63 bits of 64-bit GOT entry absolute address pub const R_LARCH_GOT64_HI12: u32 = 82; /// 12..=31 bits of TLS LE 32/64-bit offset from thread pointer pub const R_LARCH_TLS_LE_HI20: u32 = 83; /// 0..=11 bits of TLS LE 32/64-bit offset from thread pointer pub const R_LARCH_TLS_LE_LO12: u32 = 84; /// 32..=51 bits of TLS LE 64-bit offset from thread pointer pub const R_LARCH_TLS_LE64_LO20: u32 = 85; /// 52..=63 bits of TLS LE 64-bit offset from thread pointer pub const R_LARCH_TLS_LE64_HI12: u32 = 86; /// The signed 32-bit offset `offs` from `PC & 0xfffff000` to /// `(GP + IE + 0x800) & 0xfffff000`, with 12 trailing zeros removed. /// /// We define the *PC relative anchor* for the TLS IE GOT entry at /// `GP + IE` as `PC + offs` (`offs` is sign-extended to VA bits). pub const R_LARCH_TLS_IE_PC_HI20: u32 = 87; /// 0..=12 bits of the 32/64-bit offset from the /// [PC-relative anchor][R_LARCH_TLS_IE_PC_HI20] to the TLS IE GOT entry. pub const R_LARCH_TLS_IE_PC_LO12: u32 = 88; /// 32..=51 bits of the 64-bit offset from the /// [PC-relative anchor][R_LARCH_TLS_IE_PC_HI20] to the TLS IE GOT entry. pub const R_LARCH_TLS_IE64_PC_LO20: u32 = 89; /// 52..=63 bits of the 64-bit offset from the /// [PC-relative anchor][R_LARCH_TLS_IE_PC_HI20] to the TLS IE GOT entry. pub const R_LARCH_TLS_IE64_PC_HI12: u32 = 90; /// 12..=31 bits of TLS IE GOT entry 32/64-bit absolute address pub const R_LARCH_TLS_IE_HI20: u32 = 91; /// 0..=11 bits of TLS IE GOT entry 32/64-bit absolute address pub const R_LARCH_TLS_IE_LO12: u32 = 92; /// 32..=51 bits of TLS IE GOT entry 64-bit absolute address pub const R_LARCH_TLS_IE64_LO20: u32 = 93; /// 51..=63 bits of TLS IE GOT entry 64-bit absolute address pub const R_LARCH_TLS_IE64_HI12: u32 = 94; /// 12..=31 bits of the offset from `PC` to `GP + GD + 0x800`, where /// `GP + GD` is a TLS LD GOT entry pub const R_LARCH_TLS_LD_PC_HI20: u32 = 95; /// 12..=31 bits of TLS LD GOT entry 32/64-bit absolute address pub const R_LARCH_TLS_LD_HI20: u32 = 96; /// 12..=31 bits of the 32/64-bit PC-relative offset to the PC-relative /// anchor for the TLE GD GOT entry. pub const R_LARCH_TLS_GD_PC_HI20: u32 = 97; /// 12..=31 bits of TLS GD GOT entry 32/64-bit absolute address pub const R_LARCH_TLS_GD_HI20: u32 = 98; /// 32-bit PC relative pub const R_LARCH_32_PCREL: u32 = 99; /// Paired with a normal relocation at the same address to indicate the /// instruction can be relaxed pub const R_LARCH_RELAX: u32 = 100; /// Reserved pub const R_LARCH_DELETE: u32 = 101; /// Delete some bytes to ensure the instruction at PC + A aligned to /// `A.next_power_of_two()`-byte boundary pub const R_LARCH_ALIGN: u32 = 102; /// 22-bit PC-relative offset with two trailing zeros pub const R_LARCH_PCREL20_S2: u32 = 103; /// Reserved pub const R_LARCH_CFA: u32 = 104; /// 6-bit in-place addition pub const R_LARCH_ADD6: u32 = 105; /// 6-bit in-place subtraction pub const R_LARCH_SUB6: u32 = 106; /// LEB128 in-place addition pub const R_LARCH_ADD_ULEB128: u32 = 107; /// LEB128 in-place subtraction pub const R_LARCH_SUB_ULEB128: u32 = 108; /// 64-bit PC relative pub const R_LARCH_64_PCREL: u32 = 109; /// 18..=37 bits of `S + A - PC` into the `pcaddu18i` instruction at `PC`, /// and 2..=17 bits of `S + A - PC` into the `jirl` instruction at `PC + 4` pub const R_LARCH_CALL36: u32 = 110; // Xtensa values Rel*::r_type`. pub const R_XTENSA_NONE: u32 = 0; pub const R_XTENSA_32: u32 = 1; pub const R_XTENSA_RTLD: u32 = 2; pub const R_XTENSA_GLOB_DAT: u32 = 3; pub const R_XTENSA_JMP_SLOT: u32 = 4; pub const R_XTENSA_RELATIVE: u32 = 5; pub const R_XTENSA_PLT: u32 = 6; pub const R_XTENSA_OP0: u32 = 8; pub const R_XTENSA_OP1: u32 = 9; pub const R_XTENSA_OP2: u32 = 10; pub const R_XTENSA_ASM_EXPAND: u32 = 11; pub const R_XTENSA_ASM_SIMPLIFY: u32 = 12; pub const R_XTENSA_32_PCREL: u32 = 14; pub const R_XTENSA_GNU_VTINHERIT: u32 = 15; pub const R_XTENSA_GNU_VTENTRY: u32 = 16; pub const R_XTENSA_DIFF8: u32 = 17; pub const R_XTENSA_DIFF16: u32 = 18; pub const R_XTENSA_DIFF32: u32 = 19; pub const R_XTENSA_SLOT0_OP: u32 = 20; pub const R_XTENSA_SLOT1_OP: u32 = 21; pub const R_XTENSA_SLOT2_OP: u32 = 22; pub const R_XTENSA_SLOT3_OP: u32 = 23; pub const R_XTENSA_SLOT4_OP: u32 = 24; pub const R_XTENSA_SLOT5_OP: u32 = 25; pub const R_XTENSA_SLOT6_OP: u32 = 26; pub const R_XTENSA_SLOT7_OP: u32 = 27; pub const R_XTENSA_SLOT8_OP: u32 = 28; pub const R_XTENSA_SLOT9_OP: u32 = 29; pub const R_XTENSA_SLOT10_OP: u32 = 30; pub const R_XTENSA_SLOT11_OP: u32 = 31; pub const R_XTENSA_SLOT12_OP: u32 = 32; pub const R_XTENSA_SLOT13_OP: u32 = 33; pub const R_XTENSA_SLOT14_OP: u32 = 34; pub const R_XTENSA_SLOT0_ALT: u32 = 35; pub const R_XTENSA_SLOT1_ALT: u32 = 36; pub const R_XTENSA_SLOT2_ALT: u32 = 37; pub const R_XTENSA_SLOT3_ALT: u32 = 38; pub const R_XTENSA_SLOT4_ALT: u32 = 39; pub const R_XTENSA_SLOT5_ALT: u32 = 40; pub const R_XTENSA_SLOT6_ALT: u32 = 41; pub const R_XTENSA_SLOT7_ALT: u32 = 42; pub const R_XTENSA_SLOT8_ALT: u32 = 43; pub const R_XTENSA_SLOT9_ALT: u32 = 44; pub const R_XTENSA_SLOT10_ALT: u32 = 45; pub const R_XTENSA_SLOT11_ALT: u32 = 46; pub const R_XTENSA_SLOT12_ALT: u32 = 47; pub const R_XTENSA_SLOT13_ALT: u32 = 48; pub const R_XTENSA_SLOT14_ALT: u32 = 49; pub const R_XTENSA_TLSDESC_FN: u32 = 50; pub const R_XTENSA_TLSDESC_ARG: u32 = 51; pub const R_XTENSA_TLS_DTPOFF: u32 = 52; pub const R_XTENSA_TLS_TPOFF: u32 = 53; pub const R_XTENSA_TLS_FUNC: u32 = 54; pub const R_XTENSA_TLS_ARG: u32 = 55; pub const R_XTENSA_TLS_CALL: u32 = 56; pub const R_XTENSA_PDIFF8: u32 = 57; pub const R_XTENSA_PDIFF16: u32 = 58; pub const R_XTENSA_PDIFF32: u32 = 59; pub const R_XTENSA_NDIFF8: u32 = 60; pub const R_XTENSA_NDIFF16: u32 = 61; pub const R_XTENSA_NDIFF32: u32 = 62; // E2K values for `FileHeader*::e_flags`. pub const EF_E2K_IPD: u32 = 3; pub const EF_E2K_X86APP: u32 = 4; pub const EF_E2K_4MB_PAGES: u32 = 8; pub const EF_E2K_INCOMPAT: u32 = 16; pub const EF_E2K_PM: u32 = 32; pub const EF_E2K_PACK_SEGMENTS: u32 = 64; /// Encode `E_E2K_MACH_*` into `FileHeader*::e_flags`. pub const fn ef_e2k_mach_to_flag(e_flags: u32, x: u32) -> u32 { (e_flags & 0xffffff) | (x << 24) } /// Decode `E_E2K_MACH_*` from `FileHeader*::e_flags`. pub const fn ef_e2k_flag_to_mach(e_flags: u32) -> u32 { e_flags >> 24 } // Codes of supported E2K machines. /// -march=generic code. /// /// Legacy. Shouldn't be created nowadays. pub const E_E2K_MACH_BASE: u32 = 0; /// -march=elbrus-v1 code. /// /// Legacy. Shouldn't be created nowadays. pub const E_E2K_MACH_EV1: u32 = 1; /// -march=elbrus-v2 code. pub const E_E2K_MACH_EV2: u32 = 2; /// -march=elbrus-v3 code. pub const E_E2K_MACH_EV3: u32 = 3; /// -march=elbrus-v4 code. pub const E_E2K_MACH_EV4: u32 = 4; /// -march=elbrus-v5 code. pub const E_E2K_MACH_EV5: u32 = 5; /// -march=elbrus-v6 code. pub const E_E2K_MACH_EV6: u32 = 6; /// -march=elbrus-v7 code. pub const E_E2K_MACH_EV7: u32 = 7; /// -mtune=elbrus-8c code. pub const E_E2K_MACH_8C: u32 = 19; /// -mtune=elbrus-1c+ code. pub const E_E2K_MACH_1CPLUS: u32 = 20; /// -mtune=elbrus-12c code. pub const E_E2K_MACH_12C: u32 = 21; /// -mtune=elbrus-16c code. pub const E_E2K_MACH_16C: u32 = 22; /// -mtune=elbrus-2c3 code. pub const E_E2K_MACH_2C3: u32 = 23; /// -mtune=elbrus-48c code. pub const E_E2K_MACH_48C: u32 = 24; /// -mtune=elbrus-8v7 code. pub const E_E2K_MACH_8V7: u32 = 25; // E2K values `Rel*::r_type`. /// Direct 32 bit. pub const R_E2K_32_ABS: u32 = 0; /// PC relative 32 bit. pub const R_E2K_32_PC: u32 = 2; /// 32-bit offset of AP GOT entry. pub const R_E2K_AP_GOT: u32 = 3; /// 32-bit offset of PL GOT entry. pub const R_E2K_PL_GOT: u32 = 4; /// Create PLT entry. pub const R_E2K_32_JMP_SLOT: u32 = 8; /// Copy relocation, 32-bit case. pub const R_E2K_32_COPY: u32 = 9; /// Adjust by program base, 32-bit case. pub const R_E2K_32_RELATIVE: u32 = 10; /// Adjust indirectly by program base, 32-bit case. pub const R_E2K_32_IRELATIVE: u32 = 11; /// Size of symbol plus 32-bit addend. pub const R_E2K_32_SIZE: u32 = 12; /// Symbol value if resolved by the definition in the same /// compilation unit or NULL otherwise, 32-bit case. pub const R_E2K_32_DYNOPT: u32 = 13; /// Direct 64 bit. pub const R_E2K_64_ABS: u32 = 50; /// Direct 64 bit for literal. pub const R_E2K_64_ABS_LIT: u32 = 51; /// PC relative 64 bit for literal. pub const R_E2K_64_PC_LIT: u32 = 54; /// Create PLT entry, 64-bit case. pub const R_E2K_64_JMP_SLOT: u32 = 63; /// Copy relocation, 64-bit case. pub const R_E2K_64_COPY: u32 = 64; /// Adjust by program base, 64-bit case. pub const R_E2K_64_RELATIVE: u32 = 65; /// Adjust by program base for literal, 64-bit case. pub const R_E2K_64_RELATIVE_LIT: u32 = 66; /// Adjust indirectly by program base, 64-bit case. pub const R_E2K_64_IRELATIVE: u32 = 67; /// Size of symbol plus 64-bit addend. pub const R_E2K_64_SIZE: u32 = 68; /// 64-bit offset of the symbol from GOT. pub const R_E2K_64_GOTOFF: u32 = 69; /// GOT entry for ID of module containing symbol. pub const R_E2K_TLS_GDMOD: u32 = 70; /// GOT entry for offset in module TLS block. pub const R_E2K_TLS_GDREL: u32 = 71; /// Static TLS block offset GOT entry. pub const R_E2K_TLS_IE: u32 = 74; /// Offset relative to static TLS block, 32-bit case. pub const R_E2K_32_TLS_LE: u32 = 75; /// Offset relative to static TLS block, 64-bit case. pub const R_E2K_64_TLS_LE: u32 = 76; /// ID of module containing symbol, 32-bit case. pub const R_E2K_TLS_32_DTPMOD: u32 = 80; /// Offset in module TLS block, 32-bit case. pub const R_E2K_TLS_32_DTPREL: u32 = 81; /// ID of module containing symbol, 64-bit case. pub const R_E2K_TLS_64_DTPMOD: u32 = 82; /// Offset in module TLS block, 64-bit case. pub const R_E2K_TLS_64_DTPREL: u32 = 83; /// Offset in static TLS block, 32-bit case. pub const R_E2K_TLS_32_TPREL: u32 = 84; /// Offset in static TLS block, 64-bit case. pub const R_E2K_TLS_64_TPREL: u32 = 85; /// Direct AP. pub const R_E2K_AP: u32 = 100; /// Direct PL. pub const R_E2K_PL: u32 = 101; /// 32-bit offset of the symbol's entry in GOT. pub const R_E2K_GOT: u32 = 108; /// 32-bit offset of the symbol from GOT. pub const R_E2K_GOTOFF: u32 = 109; /// PC relative 28 bit for DISP. pub const R_E2K_DISP: u32 = 110; /// Prefetch insn line containing the label (symbol). pub const R_E2K_PREF: u32 = 111; /// No reloc. pub const R_E2K_NONE: u32 = 112; /// 32-bit offset of the symbol's entry in .got.plt. pub const R_E2K_GOTPLT: u32 = 114; /// Is symbol resolved locally during the link. /// The result is encoded in 5-bit ALS.src1. pub const R_E2K_ISLOCAL: u32 = 115; /// Is symbol resloved locally during the link. /// The result is encoded in a long 32-bit LTS. pub const R_E2K_ISLOCAL32: u32 = 118; /// The symbol's offset from GOT encoded within a 64-bit literal. pub const R_E2K_64_GOTOFF_LIT: u32 = 256; /// Symbol value if resolved by the definition in the same /// compilation unit or NULL otherwise, 64-bit case. pub const R_E2K_64_DYNOPT: u32 = 257; /// PC relative 64 bit in data. pub const R_E2K_64_PC: u32 = 258; // E2K values for `Dyn32::d_tag`. pub const DT_E2K_LAZY: u32 = DT_LOPROC + 1; pub const DT_E2K_LAZY_GOT: u32 = DT_LOPROC + 3; pub const DT_E2K_INIT_GOT: u32 = DT_LOPROC + 0x101c; pub const DT_E2K_EXPORT_PL: u32 = DT_LOPROC + 0x101d; pub const DT_E2K_EXPORT_PLSZ: u32 = DT_LOPROC + 0x101e; pub const DT_E2K_REAL_PLTGOT: u32 = DT_LOPROC + 0x101f; pub const DT_E2K_NO_SELFINIT: u32 = DT_LOPROC + 0x1020; pub const DT_E2K_NUM: u32 = 0x1021; #[allow(non_upper_case_globals)] pub const Tag_File: u8 = 1; #[allow(non_upper_case_globals)] pub const Tag_Section: u8 = 2; #[allow(non_upper_case_globals)] pub const Tag_Symbol: u8 = 3; unsafe_impl_endian_pod!( FileHeader32, FileHeader64, SectionHeader32, SectionHeader64, CompressionHeader32, CompressionHeader64, Sym32, Sym64, Syminfo32, Syminfo64, Rel32, Rel64, Rela32, Rela64, ProgramHeader32, ProgramHeader64, Dyn32, Dyn64, Versym, Verdef, Verdaux, Verneed, Vernaux, NoteHeader32, NoteHeader64, HashHeader, GnuHashHeader, ); object-0.36.5/src/endian.rs000064400000000000000000000567241046102023000135760ustar 00000000000000//! Types for compile-time and run-time endianness. use crate::pod::Pod; use core::fmt::{self, Debug}; use core::marker::PhantomData; /// A trait for using an endianness specification. /// /// Provides methods for converting between the specified endianness and /// the native endianness of the target machine. /// /// This trait does not require that the endianness is known at compile time. pub trait Endian: Debug + Default + Clone + Copy + PartialEq + Eq + 'static { /// Construct a specification for the endianness of some values. /// /// Returns `None` if the type does not support specifying the given endianness. fn from_big_endian(big_endian: bool) -> Option; /// Construct a specification for the endianness of some values. /// /// Returns `None` if the type does not support specifying the given endianness. fn from_little_endian(little_endian: bool) -> Option { Self::from_big_endian(!little_endian) } /// Return true for big endian byte order. fn is_big_endian(self) -> bool; /// Return true for little endian byte order. #[inline] fn is_little_endian(self) -> bool { !self.is_big_endian() } /// Converts an unsigned 16 bit integer to native endian. #[inline] fn read_u16(self, n: u16) -> u16 { if self.is_big_endian() { u16::from_be(n) } else { u16::from_le(n) } } /// Converts an unsigned 32 bit integer to native endian. #[inline] fn read_u32(self, n: u32) -> u32 { if self.is_big_endian() { u32::from_be(n) } else { u32::from_le(n) } } /// Converts an unsigned 64 bit integer to native endian. #[inline] fn read_u64(self, n: u64) -> u64 { if self.is_big_endian() { u64::from_be(n) } else { u64::from_le(n) } } /// Converts a signed 16 bit integer to native endian. #[inline] fn read_i16(self, n: i16) -> i16 { if self.is_big_endian() { i16::from_be(n) } else { i16::from_le(n) } } /// Converts a signed 32 bit integer to native endian. #[inline] fn read_i32(self, n: i32) -> i32 { if self.is_big_endian() { i32::from_be(n) } else { i32::from_le(n) } } /// Converts a signed 64 bit integer to native endian. #[inline] fn read_i64(self, n: i64) -> i64 { if self.is_big_endian() { i64::from_be(n) } else { i64::from_le(n) } } /// Converts an unaligned unsigned 16 bit integer to native endian. #[inline] fn read_u16_bytes(self, n: [u8; 2]) -> u16 { if self.is_big_endian() { u16::from_be_bytes(n) } else { u16::from_le_bytes(n) } } /// Converts an unaligned unsigned 32 bit integer to native endian. #[inline] fn read_u32_bytes(self, n: [u8; 4]) -> u32 { if self.is_big_endian() { u32::from_be_bytes(n) } else { u32::from_le_bytes(n) } } /// Converts an unaligned unsigned 64 bit integer to native endian. #[inline] fn read_u64_bytes(self, n: [u8; 8]) -> u64 { if self.is_big_endian() { u64::from_be_bytes(n) } else { u64::from_le_bytes(n) } } /// Converts an unaligned signed 16 bit integer to native endian. #[inline] fn read_i16_bytes(self, n: [u8; 2]) -> i16 { if self.is_big_endian() { i16::from_be_bytes(n) } else { i16::from_le_bytes(n) } } /// Converts an unaligned signed 32 bit integer to native endian. #[inline] fn read_i32_bytes(self, n: [u8; 4]) -> i32 { if self.is_big_endian() { i32::from_be_bytes(n) } else { i32::from_le_bytes(n) } } /// Converts an unaligned signed 64 bit integer to native endian. #[inline] fn read_i64_bytes(self, n: [u8; 8]) -> i64 { if self.is_big_endian() { i64::from_be_bytes(n) } else { i64::from_le_bytes(n) } } /// Converts an unsigned 16 bit integer from native endian. #[inline] fn write_u16(self, n: u16) -> u16 { if self.is_big_endian() { u16::to_be(n) } else { u16::to_le(n) } } /// Converts an unsigned 32 bit integer from native endian. #[inline] fn write_u32(self, n: u32) -> u32 { if self.is_big_endian() { u32::to_be(n) } else { u32::to_le(n) } } /// Converts an unsigned 64 bit integer from native endian. #[inline] fn write_u64(self, n: u64) -> u64 { if self.is_big_endian() { u64::to_be(n) } else { u64::to_le(n) } } /// Converts a signed 16 bit integer from native endian. #[inline] fn write_i16(self, n: i16) -> i16 { if self.is_big_endian() { i16::to_be(n) } else { i16::to_le(n) } } /// Converts a signed 32 bit integer from native endian. #[inline] fn write_i32(self, n: i32) -> i32 { if self.is_big_endian() { i32::to_be(n) } else { i32::to_le(n) } } /// Converts a signed 64 bit integer from native endian. #[inline] fn write_i64(self, n: i64) -> i64 { if self.is_big_endian() { i64::to_be(n) } else { i64::to_le(n) } } /// Converts an unaligned unsigned 16 bit integer from native endian. #[inline] fn write_u16_bytes(self, n: u16) -> [u8; 2] { if self.is_big_endian() { u16::to_be_bytes(n) } else { u16::to_le_bytes(n) } } /// Converts an unaligned unsigned 32 bit integer from native endian. #[inline] fn write_u32_bytes(self, n: u32) -> [u8; 4] { if self.is_big_endian() { u32::to_be_bytes(n) } else { u32::to_le_bytes(n) } } /// Converts an unaligned unsigned 64 bit integer from native endian. #[inline] fn write_u64_bytes(self, n: u64) -> [u8; 8] { if self.is_big_endian() { u64::to_be_bytes(n) } else { u64::to_le_bytes(n) } } /// Converts an unaligned signed 16 bit integer from native endian. #[inline] fn write_i16_bytes(self, n: i16) -> [u8; 2] { if self.is_big_endian() { i16::to_be_bytes(n) } else { i16::to_le_bytes(n) } } /// Converts an unaligned signed 32 bit integer from native endian. #[inline] fn write_i32_bytes(self, n: i32) -> [u8; 4] { if self.is_big_endian() { i32::to_be_bytes(n) } else { i32::to_le_bytes(n) } } /// Converts an unaligned signed 64 bit integer from native endian. #[inline] fn write_i64_bytes(self, n: i64) -> [u8; 8] { if self.is_big_endian() { i64::to_be_bytes(n) } else { i64::to_le_bytes(n) } } } /// An endianness that is selectable at run-time. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Endianness { /// Little endian byte order. Little, /// Big endian byte order. Big, } impl Default for Endianness { #[cfg(target_endian = "little")] #[inline] fn default() -> Endianness { Endianness::Little } #[cfg(target_endian = "big")] #[inline] fn default() -> Endianness { Endianness::Big } } impl Endian for Endianness { #[inline] fn from_big_endian(big_endian: bool) -> Option { Some(if big_endian { Endianness::Big } else { Endianness::Little }) } #[inline] fn is_big_endian(self) -> bool { self != Endianness::Little } } /// Compile-time little endian byte order. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct LittleEndian; impl Default for LittleEndian { #[inline] fn default() -> LittleEndian { LittleEndian } } impl Endian for LittleEndian { #[inline] fn from_big_endian(big_endian: bool) -> Option { if big_endian { None } else { Some(LittleEndian) } } #[inline] fn is_big_endian(self) -> bool { false } } /// Compile-time big endian byte order. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct BigEndian; impl Default for BigEndian { #[inline] fn default() -> BigEndian { BigEndian } } impl Endian for BigEndian { #[inline] fn from_big_endian(big_endian: bool) -> Option { if big_endian { Some(BigEndian) } else { None } } #[inline] fn is_big_endian(self) -> bool { true } } /// The native endianness for the target platform. #[cfg(target_endian = "little")] pub type NativeEndian = LittleEndian; #[cfg(target_endian = "little")] #[allow(non_upper_case_globals)] #[doc(hidden)] pub const NativeEndian: LittleEndian = LittleEndian; /// The native endianness for the target platform. #[cfg(target_endian = "big")] pub type NativeEndian = BigEndian; #[cfg(target_endian = "big")] #[allow(non_upper_case_globals)] #[doc(hidden)] pub const NativeEndian: BigEndian = BigEndian; macro_rules! unsafe_impl_endian_pod { ($($struct_name:ident),+ $(,)?) => { $( unsafe impl Pod for $struct_name { } )+ } } #[cfg(not(feature = "unaligned"))] mod aligned { use super::{fmt, Endian, PhantomData, Pod}; /// A `u16` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct U16(u16, PhantomData); impl U16 { /// Construct a new value given bytes that already have the required endianness. pub const fn from_bytes(n: [u8; 2]) -> Self { Self(u16::from_ne_bytes(n), PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: u16) -> Self { Self(e.write_u16(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> u16 { e.read_u16(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: u16) { self.0 = e.write_u16(n); } } /// A `u32` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct U32(u32, PhantomData); impl U32 { /// Construct a new value given bytes that already have the required endianness. pub const fn from_bytes(n: [u8; 4]) -> Self { Self(u32::from_ne_bytes(n), PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: u32) -> Self { Self(e.write_u32(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> u32 { e.read_u32(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: u32) { self.0 = e.write_u32(n); } } /// A `u64` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct U64(u64, PhantomData); impl U64 { /// Construct a new value given bytes that already have the required endianness. pub const fn from_bytes(n: [u8; 8]) -> Self { Self(u64::from_ne_bytes(n), PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: u64) -> Self { Self(e.write_u64(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> u64 { e.read_u64(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: u64) { self.0 = e.write_u64(n); } } /// An `i16` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct I16(i16, PhantomData); impl I16 { /// Construct a new value given bytes that already have the required endianness. pub const fn from_bytes(n: [u8; 2]) -> Self { Self(i16::from_ne_bytes(n), PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: i16) -> Self { Self(e.write_i16(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> i16 { e.read_i16(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: i16) { self.0 = e.write_i16(n); } } /// An `i32` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct I32(i32, PhantomData); impl I32 { /// Construct a new value given bytes that already have the required endianness. pub const fn from_bytes(n: [u8; 4]) -> Self { Self(i32::from_ne_bytes(n), PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: i32) -> Self { Self(e.write_i32(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> i32 { e.read_i32(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: i32) { self.0 = e.write_i32(n); } } /// An `i64` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct I64(i64, PhantomData); impl I64 { /// Construct a new value given bytes that already have the required endianness. pub const fn from_bytes(n: [u8; 8]) -> Self { Self(i64::from_ne_bytes(n), PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: i64) -> Self { Self(e.write_i64(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> i64 { e.read_i64(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: i64) { self.0 = e.write_i64(n); } } impl fmt::Debug for U16 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "U16({:x})", self.0) } } impl fmt::Debug for U32 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "U32({:x})", self.0) } } impl fmt::Debug for U64 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "U64({:x})", self.0) } } impl fmt::Debug for I16 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "I16({:x})", self.0) } } impl fmt::Debug for I32 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "I32({:x})", self.0) } } impl fmt::Debug for I64 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "I64({:x})", self.0) } } unsafe_impl_endian_pod!(U16, U32, U64, I16, I32, I64); } #[cfg(not(feature = "unaligned"))] pub use aligned::*; /// A `u16` value with an externally specified endianness of type `E`. #[cfg(feature = "unaligned")] pub type U16 = U16Bytes; /// A `u32` value with an externally specified endianness of type `E`. #[cfg(feature = "unaligned")] pub type U32 = U32Bytes; /// A `u64` value with an externally specified endianness of type `E`. #[cfg(feature = "unaligned")] pub type U64 = U64Bytes; /// An `i16` value with an externally specified endianness of type `E`. #[cfg(feature = "unaligned")] pub type I16 = I16Bytes; /// An `i32` value with an externally specified endianness of type `E`. #[cfg(feature = "unaligned")] pub type I32 = I32Bytes; /// An `i64` value with an externally specified endianness of type `E`. #[cfg(feature = "unaligned")] pub type I64 = I64Bytes; /// An unaligned `u16` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct U16Bytes([u8; 2], PhantomData); impl U16Bytes { /// Construct a new value given bytes that already have the required endianness. pub const fn from_bytes(n: [u8; 2]) -> Self { Self(n, PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: u16) -> Self { Self(e.write_u16_bytes(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> u16 { e.read_u16_bytes(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: u16) { self.0 = e.write_u16_bytes(n); } } /// An unaligned `u32` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct U32Bytes([u8; 4], PhantomData); impl U32Bytes { /// Construct a new value given bytes that already have the required endianness. pub const fn from_bytes(n: [u8; 4]) -> Self { Self(n, PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: u32) -> Self { Self(e.write_u32_bytes(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> u32 { e.read_u32_bytes(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: u32) { self.0 = e.write_u32_bytes(n); } } /// An unaligned `u64` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct U64Bytes([u8; 8], PhantomData); impl U64Bytes { /// Construct a new value given bytes that already have the required endianness. pub const fn from_bytes(n: [u8; 8]) -> Self { Self(n, PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: u64) -> Self { Self(e.write_u64_bytes(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> u64 { e.read_u64_bytes(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: u64) { self.0 = e.write_u64_bytes(n); } } /// An unaligned `i16` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct I16Bytes([u8; 2], PhantomData); impl I16Bytes { /// Construct a new value given bytes that already have the required endianness. pub const fn from_bytes(n: [u8; 2]) -> Self { Self(n, PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: i16) -> Self { Self(e.write_i16_bytes(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> i16 { e.read_i16_bytes(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: i16) { self.0 = e.write_i16_bytes(n); } } /// An unaligned `i32` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct I32Bytes([u8; 4], PhantomData); impl I32Bytes { /// Construct a new value given bytes that already have the required endianness. pub const fn from_bytes(n: [u8; 4]) -> Self { Self(n, PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: i32) -> Self { Self(e.write_i32_bytes(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> i32 { e.read_i32_bytes(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: i32) { self.0 = e.write_i32_bytes(n); } } /// An unaligned `i64` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct I64Bytes([u8; 8], PhantomData); impl I64Bytes { /// Construct a new value given bytes that already have the required endianness. pub const fn from_bytes(n: [u8; 8]) -> Self { Self(n, PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: i64) -> Self { Self(e.write_i64_bytes(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> i64 { e.read_i64_bytes(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: i64) { self.0 = e.write_i64_bytes(n); } } impl fmt::Debug for U16Bytes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "U16({:x}, {:x})", self.0[0], self.0[1],) } } impl fmt::Debug for U32Bytes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "U32({:x}, {:x}, {:x}, {:x})", self.0[0], self.0[1], self.0[2], self.0[3], ) } } impl fmt::Debug for U64Bytes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "U64({:x}, {:x}, {:x}, {:x}, {:x}, {:x}, {:x}, {:x})", self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], self.0[6], self.0[7], ) } } impl fmt::Debug for I16Bytes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "I16({:x}, {:x})", self.0[0], self.0[1],) } } impl fmt::Debug for I32Bytes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "I32({:x}, {:x}, {:x}, {:x})", self.0[0], self.0[1], self.0[2], self.0[3], ) } } impl fmt::Debug for I64Bytes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "I64({:x}, {:x}, {:x}, {:x}, {:x}, {:x}, {:x}, {:x})", self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], self.0[6], self.0[7], ) } } unsafe_impl_endian_pod!(U16Bytes, U32Bytes, U64Bytes, I16Bytes, I32Bytes, I64Bytes); object-0.36.5/src/lib.rs000064400000000000000000000062061046102023000130740ustar 00000000000000//! # `object` //! //! The `object` crate provides a unified interface to working with object files //! across platforms. It supports reading relocatable object files and executable files, //! and writing relocatable object files and some executable files. //! //! ## Raw struct definitions //! //! Raw structs are defined for: [ELF](elf), [Mach-O](macho), [PE/COFF](pe), //! [XCOFF](xcoff), [archive]. //! Types and traits for zerocopy support are defined in the [`pod`] and [`endian`] modules. //! //! ## Unified read API //! //! The [`read`] module provides a unified read API using the [`read::Object`] trait. //! There is an implementation of this trait for [`read::File`], which allows reading any //! file format, as well as implementations for each file format. //! //! ## Low level read API //! //! The [`read#modules`] submodules define helpers that operate on the raw structs. //! These can be used instead of the unified API, or in conjunction with it to access //! details that are not available via the unified API. //! //! ## Unified write API //! //! The [`mod@write`] module provides a unified write API for relocatable object files //! using [`write::Object`]. This does not support writing executable files. //! //! ## Low level write API //! //! The [`mod@write#modules`] submodules define helpers for writing the raw structs. //! //! ## Build API //! //! The [`mod@build`] submodules define helpers for building object files, either from //! scratch or by modifying existing files. //! //! ## Shared definitions //! //! The crate provides a number of definitions that are used by both the read and write //! APIs. These are defined at the top level module, but none of these are the main entry //! points of the crate. #![deny(missing_docs)] #![deny(missing_debug_implementations)] #![no_std] #![warn(rust_2018_idioms)] // Style. #![allow(clippy::collapsible_else_if)] #![allow(clippy::collapsible_if)] #![allow(clippy::collapsible_match)] #![allow(clippy::comparison_chain)] #![allow(clippy::field_reassign_with_default)] #![allow(clippy::manual_flatten)] #![allow(clippy::match_like_matches_macro)] #![allow(clippy::single_match)] #![allow(clippy::type_complexity)] // Occurs due to fallible iteration. #![allow(clippy::should_implement_trait)] // Unit errors are converted to other types by callers. #![allow(clippy::result_unit_err)] #[cfg(feature = "cargo-all")] compile_error!("'--all-features' is not supported; use '--features all' instead"); #[cfg(any(feature = "read_core", feature = "write_core"))] #[allow(unused_imports)] #[macro_use] extern crate alloc; #[cfg(feature = "std")] #[allow(unused_imports)] #[macro_use] extern crate std; mod common; pub use common::*; #[macro_use] pub mod endian; pub use endian::*; #[macro_use] pub mod pod; pub use pod::*; #[cfg(feature = "read_core")] pub mod read; #[cfg(feature = "read_core")] pub use read::*; #[cfg(feature = "write_core")] pub mod write; #[cfg(feature = "build_core")] pub mod build; #[cfg(feature = "archive")] pub mod archive; #[cfg(feature = "elf")] pub mod elf; #[cfg(feature = "macho")] pub mod macho; #[cfg(any(feature = "coff", feature = "pe"))] pub mod pe; #[cfg(feature = "xcoff")] pub mod xcoff; object-0.36.5/src/macho.rs000064400000000000000000003720041046102023000134170ustar 00000000000000//! Mach-O definitions. //! //! These definitions are independent of read/write support, although we do implement //! some traits useful for those. //! //! This module is based heavily on header files from MacOSX11.1.sdk. #![allow(missing_docs)] use crate::endian::{BigEndian, Endian, U64Bytes, U16, U32, U64}; use crate::pod::Pod; // Definitions from "/usr/include/mach/machine.h". /* * Capability bits used in the definition of cpu_type. */ /// mask for architecture bits pub const CPU_ARCH_MASK: u32 = 0xff00_0000; /// 64 bit ABI pub const CPU_ARCH_ABI64: u32 = 0x0100_0000; /// ABI for 64-bit hardware with 32-bit types; LP32 pub const CPU_ARCH_ABI64_32: u32 = 0x0200_0000; /* * Machine types known by all. */ pub const CPU_TYPE_ANY: u32 = !0; pub const CPU_TYPE_VAX: u32 = 1; pub const CPU_TYPE_MC680X0: u32 = 6; pub const CPU_TYPE_X86: u32 = 7; pub const CPU_TYPE_X86_64: u32 = CPU_TYPE_X86 | CPU_ARCH_ABI64; pub const CPU_TYPE_MIPS: u32 = 8; pub const CPU_TYPE_MC98000: u32 = 10; pub const CPU_TYPE_HPPA: u32 = 11; pub const CPU_TYPE_ARM: u32 = 12; pub const CPU_TYPE_ARM64: u32 = CPU_TYPE_ARM | CPU_ARCH_ABI64; pub const CPU_TYPE_ARM64_32: u32 = CPU_TYPE_ARM | CPU_ARCH_ABI64_32; pub const CPU_TYPE_MC88000: u32 = 13; pub const CPU_TYPE_SPARC: u32 = 14; pub const CPU_TYPE_I860: u32 = 15; pub const CPU_TYPE_ALPHA: u32 = 16; pub const CPU_TYPE_POWERPC: u32 = 18; pub const CPU_TYPE_POWERPC64: u32 = CPU_TYPE_POWERPC | CPU_ARCH_ABI64; /* * Capability bits used in the definition of cpu_subtype. */ /// mask for feature flags pub const CPU_SUBTYPE_MASK: u32 = 0xff00_0000; /// 64 bit libraries pub const CPU_SUBTYPE_LIB64: u32 = 0x8000_0000; /// pointer authentication with versioned ABI pub const CPU_SUBTYPE_PTRAUTH_ABI: u32 = 0x8000_0000; /// When selecting a slice, ANY will pick the slice with the best /// grading for the selected cpu_type_t, unlike the "ALL" subtypes, /// which are the slices that can run on any hardware for that cpu type. pub const CPU_SUBTYPE_ANY: u32 = !0; /* * Object files that are hand-crafted to run on any * implementation of an architecture are tagged with * CPU_SUBTYPE_MULTIPLE. This functions essentially the same as * the "ALL" subtype of an architecture except that it allows us * to easily find object files that may need to be modified * whenever a new implementation of an architecture comes out. * * It is the responsibility of the implementor to make sure the * software handles unsupported implementations elegantly. */ pub const CPU_SUBTYPE_MULTIPLE: u32 = !0; pub const CPU_SUBTYPE_LITTLE_ENDIAN: u32 = 0; pub const CPU_SUBTYPE_BIG_ENDIAN: u32 = 1; /* * VAX subtypes (these do *not* necessary conform to the actual cpu * ID assigned by DEC available via the SID register). */ pub const CPU_SUBTYPE_VAX_ALL: u32 = 0; pub const CPU_SUBTYPE_VAX780: u32 = 1; pub const CPU_SUBTYPE_VAX785: u32 = 2; pub const CPU_SUBTYPE_VAX750: u32 = 3; pub const CPU_SUBTYPE_VAX730: u32 = 4; pub const CPU_SUBTYPE_UVAXI: u32 = 5; pub const CPU_SUBTYPE_UVAXII: u32 = 6; pub const CPU_SUBTYPE_VAX8200: u32 = 7; pub const CPU_SUBTYPE_VAX8500: u32 = 8; pub const CPU_SUBTYPE_VAX8600: u32 = 9; pub const CPU_SUBTYPE_VAX8650: u32 = 10; pub const CPU_SUBTYPE_VAX8800: u32 = 11; pub const CPU_SUBTYPE_UVAXIII: u32 = 12; /* * 680x0 subtypes * * The subtype definitions here are unusual for historical reasons. * NeXT used to consider 68030 code as generic 68000 code. For * backwards compatibility: * * CPU_SUBTYPE_MC68030 symbol has been preserved for source code * compatibility. * * CPU_SUBTYPE_MC680x0_ALL has been defined to be the same * subtype as CPU_SUBTYPE_MC68030 for binary comatability. * * CPU_SUBTYPE_MC68030_ONLY has been added to allow new object * files to be tagged as containing 68030-specific instructions. */ pub const CPU_SUBTYPE_MC680X0_ALL: u32 = 1; // compat pub const CPU_SUBTYPE_MC68030: u32 = 1; pub const CPU_SUBTYPE_MC68040: u32 = 2; pub const CPU_SUBTYPE_MC68030_ONLY: u32 = 3; /* * I386 subtypes */ #[inline] pub const fn cpu_subtype_intel(f: u32, m: u32) -> u32 { f + (m << 4) } pub const CPU_SUBTYPE_I386_ALL: u32 = cpu_subtype_intel(3, 0); pub const CPU_SUBTYPE_386: u32 = cpu_subtype_intel(3, 0); pub const CPU_SUBTYPE_486: u32 = cpu_subtype_intel(4, 0); pub const CPU_SUBTYPE_486SX: u32 = cpu_subtype_intel(4, 8); pub const CPU_SUBTYPE_586: u32 = cpu_subtype_intel(5, 0); pub const CPU_SUBTYPE_PENT: u32 = cpu_subtype_intel(5, 0); pub const CPU_SUBTYPE_PENTPRO: u32 = cpu_subtype_intel(6, 1); pub const CPU_SUBTYPE_PENTII_M3: u32 = cpu_subtype_intel(6, 3); pub const CPU_SUBTYPE_PENTII_M5: u32 = cpu_subtype_intel(6, 5); pub const CPU_SUBTYPE_CELERON: u32 = cpu_subtype_intel(7, 6); pub const CPU_SUBTYPE_CELERON_MOBILE: u32 = cpu_subtype_intel(7, 7); pub const CPU_SUBTYPE_PENTIUM_3: u32 = cpu_subtype_intel(8, 0); pub const CPU_SUBTYPE_PENTIUM_3_M: u32 = cpu_subtype_intel(8, 1); pub const CPU_SUBTYPE_PENTIUM_3_XEON: u32 = cpu_subtype_intel(8, 2); pub const CPU_SUBTYPE_PENTIUM_M: u32 = cpu_subtype_intel(9, 0); pub const CPU_SUBTYPE_PENTIUM_4: u32 = cpu_subtype_intel(10, 0); pub const CPU_SUBTYPE_PENTIUM_4_M: u32 = cpu_subtype_intel(10, 1); pub const CPU_SUBTYPE_ITANIUM: u32 = cpu_subtype_intel(11, 0); pub const CPU_SUBTYPE_ITANIUM_2: u32 = cpu_subtype_intel(11, 1); pub const CPU_SUBTYPE_XEON: u32 = cpu_subtype_intel(12, 0); pub const CPU_SUBTYPE_XEON_MP: u32 = cpu_subtype_intel(12, 1); #[inline] pub const fn cpu_subtype_intel_family(x: u32) -> u32 { x & 15 } pub const CPU_SUBTYPE_INTEL_FAMILY_MAX: u32 = 15; #[inline] pub const fn cpu_subtype_intel_model(x: u32) -> u32 { x >> 4 } pub const CPU_SUBTYPE_INTEL_MODEL_ALL: u32 = 0; /* * X86 subtypes. */ pub const CPU_SUBTYPE_X86_ALL: u32 = 3; pub const CPU_SUBTYPE_X86_64_ALL: u32 = 3; pub const CPU_SUBTYPE_X86_ARCH1: u32 = 4; /// Haswell feature subset pub const CPU_SUBTYPE_X86_64_H: u32 = 8; /* * Mips subtypes. */ pub const CPU_SUBTYPE_MIPS_ALL: u32 = 0; pub const CPU_SUBTYPE_MIPS_R2300: u32 = 1; pub const CPU_SUBTYPE_MIPS_R2600: u32 = 2; pub const CPU_SUBTYPE_MIPS_R2800: u32 = 3; /// pmax pub const CPU_SUBTYPE_MIPS_R2000A: u32 = 4; pub const CPU_SUBTYPE_MIPS_R2000: u32 = 5; /// 3max pub const CPU_SUBTYPE_MIPS_R3000A: u32 = 6; pub const CPU_SUBTYPE_MIPS_R3000: u32 = 7; /* * MC98000 (PowerPC) subtypes */ pub const CPU_SUBTYPE_MC98000_ALL: u32 = 0; pub const CPU_SUBTYPE_MC98601: u32 = 1; /* * HPPA subtypes for Hewlett-Packard HP-PA family of * risc processors. Port by NeXT to 700 series. */ pub const CPU_SUBTYPE_HPPA_ALL: u32 = 0; pub const CPU_SUBTYPE_HPPA_7100LC: u32 = 1; /* * MC88000 subtypes. */ pub const CPU_SUBTYPE_MC88000_ALL: u32 = 0; pub const CPU_SUBTYPE_MC88100: u32 = 1; pub const CPU_SUBTYPE_MC88110: u32 = 2; /* * SPARC subtypes */ pub const CPU_SUBTYPE_SPARC_ALL: u32 = 0; /* * I860 subtypes */ pub const CPU_SUBTYPE_I860_ALL: u32 = 0; pub const CPU_SUBTYPE_I860_860: u32 = 1; /* * PowerPC subtypes */ pub const CPU_SUBTYPE_POWERPC_ALL: u32 = 0; pub const CPU_SUBTYPE_POWERPC_601: u32 = 1; pub const CPU_SUBTYPE_POWERPC_602: u32 = 2; pub const CPU_SUBTYPE_POWERPC_603: u32 = 3; pub const CPU_SUBTYPE_POWERPC_603E: u32 = 4; pub const CPU_SUBTYPE_POWERPC_603EV: u32 = 5; pub const CPU_SUBTYPE_POWERPC_604: u32 = 6; pub const CPU_SUBTYPE_POWERPC_604E: u32 = 7; pub const CPU_SUBTYPE_POWERPC_620: u32 = 8; pub const CPU_SUBTYPE_POWERPC_750: u32 = 9; pub const CPU_SUBTYPE_POWERPC_7400: u32 = 10; pub const CPU_SUBTYPE_POWERPC_7450: u32 = 11; pub const CPU_SUBTYPE_POWERPC_970: u32 = 100; /* * ARM subtypes */ pub const CPU_SUBTYPE_ARM_ALL: u32 = 0; pub const CPU_SUBTYPE_ARM_V4T: u32 = 5; pub const CPU_SUBTYPE_ARM_V6: u32 = 6; pub const CPU_SUBTYPE_ARM_V5TEJ: u32 = 7; pub const CPU_SUBTYPE_ARM_XSCALE: u32 = 8; /// ARMv7-A and ARMv7-R pub const CPU_SUBTYPE_ARM_V7: u32 = 9; /// Cortex A9 pub const CPU_SUBTYPE_ARM_V7F: u32 = 10; /// Swift pub const CPU_SUBTYPE_ARM_V7S: u32 = 11; pub const CPU_SUBTYPE_ARM_V7K: u32 = 12; pub const CPU_SUBTYPE_ARM_V8: u32 = 13; /// Not meant to be run under xnu pub const CPU_SUBTYPE_ARM_V6M: u32 = 14; /// Not meant to be run under xnu pub const CPU_SUBTYPE_ARM_V7M: u32 = 15; /// Not meant to be run under xnu pub const CPU_SUBTYPE_ARM_V7EM: u32 = 16; /// Not meant to be run under xnu pub const CPU_SUBTYPE_ARM_V8M: u32 = 17; /* * ARM64 subtypes */ pub const CPU_SUBTYPE_ARM64_ALL: u32 = 0; pub const CPU_SUBTYPE_ARM64_V8: u32 = 1; pub const CPU_SUBTYPE_ARM64E: u32 = 2; /* * ARM64_32 subtypes */ pub const CPU_SUBTYPE_ARM64_32_ALL: u32 = 0; pub const CPU_SUBTYPE_ARM64_32_V8: u32 = 1; // Definitions from "/usr/include/mach/vm_prot.h". /// read permission pub const VM_PROT_READ: u32 = 0x01; /// write permission pub const VM_PROT_WRITE: u32 = 0x02; /// execute permission pub const VM_PROT_EXECUTE: u32 = 0x04; // Definitions from https://opensource.apple.com/source/dyld/dyld-210.2.3/launch-cache/dyld_cache_format.h.auto.html /// The dyld cache header. /// Corresponds to struct dyld_cache_header from dyld_cache_format.h. /// This header has grown over time. Only the fields up to and including dyld_base_address /// are guaranteed to be present. For all other fields, check the header size before /// accessing the field. The header size is stored in mapping_offset; the mappings start /// right after the theader. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct DyldCacheHeader { /// e.g. "dyld_v0 i386" pub magic: [u8; 16], /// file offset to first dyld_cache_mapping_info pub mapping_offset: U32, // offset: 0x10 /// number of dyld_cache_mapping_info entries pub mapping_count: U32, // offset: 0x14 /// file offset to first dyld_cache_image_info pub images_offset: U32, // offset: 0x18 /// number of dyld_cache_image_info entries pub images_count: U32, // offset: 0x1c /// base address of dyld when cache was built pub dyld_base_address: U64, // offset: 0x20 reserved1: [u8; 32], // offset: 0x28 /// file offset of where local symbols are stored pub local_symbols_offset: U64, // offset: 0x48 /// size of local symbols information pub local_symbols_size: U64, // offset: 0x50 /// unique value for each shared cache file pub uuid: [u8; 16], // offset: 0x58 reserved2: [u8; 32], // offset: 0x68 reserved3: [u8; 32], // offset: 0x88 reserved4: [u8; 32], // offset: 0xa8 reserved5: [u8; 32], // offset: 0xc8 reserved6: [u8; 32], // offset: 0xe8 reserved7: [u8; 32], // offset: 0x108 reserved8: [u8; 32], // offset: 0x128 reserved9: [u8; 32], // offset: 0x148 reserved10: [u8; 32], // offset: 0x168 /// file offset to first dyld_subcache_info pub subcaches_offset: U32, // offset: 0x188 /// number of dyld_subcache_info entries pub subcaches_count: U32, // offset: 0x18c /// the UUID of the .symbols subcache pub symbols_subcache_uuid: [u8; 16], // offset: 0x190 reserved11: [u8; 32], // offset: 0x1a0 /// file offset to first dyld_cache_image_info /// Use this instead of images_offset if mapping_offset is at least 0x1c4. pub images_across_all_subcaches_offset: U32, // offset: 0x1c0 /// number of dyld_cache_image_info entries /// Use this instead of images_count if mapping_offset is at least 0x1c4. pub images_across_all_subcaches_count: U32, // offset: 0x1c4 } /// Corresponds to struct dyld_cache_mapping_info from dyld_cache_format.h. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct DyldCacheMappingInfo { pub address: U64, pub size: U64, pub file_offset: U64, pub max_prot: U32, pub init_prot: U32, } /// Corresponds to struct dyld_cache_image_info from dyld_cache_format.h. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct DyldCacheImageInfo { pub address: U64, pub mod_time: U64, pub inode: U64, pub path_file_offset: U32, pub pad: U32, } /// Added in dyld-940, which shipped with macOS 12 / iOS 15. /// Originally called `dyld_subcache_entry`, renamed to `dyld_subcache_entry_v1` /// in dyld-1042.1. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct DyldSubCacheEntryV1 { /// The UUID of this subcache. pub uuid: [u8; 16], /// The offset of this subcache from the main cache base address. pub cache_vm_offset: U64, } /// Added in dyld-1042.1, which shipped with macOS 13 / iOS 16. /// Called `dyld_subcache_entry` as of dyld-1042.1. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct DyldSubCacheEntryV2 { /// The UUID of this subcache. pub uuid: [u8; 16], /// The offset of this subcache from the main cache base address. pub cache_vm_offset: U64, /// The file name suffix of the subCache file, e.g. ".25.data" or ".03.development". pub file_suffix: [u8; 32], } // Definitions from "/usr/include/mach-o/loader.h". /* * This header file describes the structures of the file format for "fat" * architecture specific file (wrapper design). At the beginning of the file * there is one `FatHeader` structure followed by a number of `FatArch*` * structures. For each architecture in the file, specified by a pair of * cputype and cpusubtype, the `FatHeader` describes the file offset, file * size and alignment in the file of the architecture specific member. * The padded bytes in the file to place each member on it's specific alignment * are defined to be read as zeros and can be left as "holes" if the file system * can support them as long as they read as zeros. * * All structures defined here are always written and read to/from disk * in big-endian order. */ pub const FAT_MAGIC: u32 = 0xcafe_babe; /// NXSwapLong(FAT_MAGIC) pub const FAT_CIGAM: u32 = 0xbeba_feca; #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct FatHeader { /// FAT_MAGIC or FAT_MAGIC_64 pub magic: U32, /// number of structs that follow pub nfat_arch: U32, } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct FatArch32 { /// cpu specifier (int) pub cputype: U32, /// machine specifier (int) pub cpusubtype: U32, /// file offset to this object file pub offset: U32, /// size of this object file pub size: U32, /// alignment as a power of 2 pub align: U32, } /* * The support for the 64-bit fat file format described here is a work in * progress and not yet fully supported in all the Apple Developer Tools. * * When a slice is greater than 4mb or an offset to a slice is greater than 4mb * then the 64-bit fat file format is used. */ pub const FAT_MAGIC_64: u32 = 0xcafe_babf; /// NXSwapLong(FAT_MAGIC_64) pub const FAT_CIGAM_64: u32 = 0xbfba_feca; #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct FatArch64 { /// cpu specifier (int) pub cputype: U32, /// machine specifier (int) pub cpusubtype: U32, /// file offset to this object file pub offset: U64, /// size of this object file pub size: U64, /// alignment as a power of 2 pub align: U32, /// reserved pub reserved: U32, } // Definitions from "/usr/include/mach-o/loader.h". /// The 32-bit mach header. /// /// Appears at the very beginning of the object file for 32-bit architectures. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct MachHeader32 { /// mach magic number identifier pub magic: U32, /// cpu specifier pub cputype: U32, /// machine specifier pub cpusubtype: U32, /// type of file pub filetype: U32, /// number of load commands pub ncmds: U32, /// the size of all the load commands pub sizeofcmds: U32, /// flags pub flags: U32, } // Values for `MachHeader32::magic`. /// the mach magic number pub const MH_MAGIC: u32 = 0xfeed_face; /// NXSwapInt(MH_MAGIC) pub const MH_CIGAM: u32 = 0xcefa_edfe; /// The 64-bit mach header. /// /// Appears at the very beginning of object files for 64-bit architectures. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct MachHeader64 { /// mach magic number identifier pub magic: U32, /// cpu specifier pub cputype: U32, /// machine specifier pub cpusubtype: U32, /// type of file pub filetype: U32, /// number of load commands pub ncmds: U32, /// the size of all the load commands pub sizeofcmds: U32, /// flags pub flags: U32, /// reserved pub reserved: U32, } // Values for `MachHeader64::magic`. /// the 64-bit mach magic number pub const MH_MAGIC_64: u32 = 0xfeed_facf; /// NXSwapInt(MH_MAGIC_64) pub const MH_CIGAM_64: u32 = 0xcffa_edfe; /* * The layout of the file depends on the filetype. For all but the MH_OBJECT * file type the segments are padded out and aligned on a segment alignment * boundary for efficient demand pageing. The MH_EXECUTE, MH_FVMLIB, MH_DYLIB, * MH_DYLINKER and MH_BUNDLE file types also have the headers included as part * of their first segment. * * The file type MH_OBJECT is a compact format intended as output of the * assembler and input (and possibly output) of the link editor (the .o * format). All sections are in one unnamed segment with no segment padding. * This format is used as an executable format when the file is so small the * segment padding greatly increases its size. * * The file type MH_PRELOAD is an executable format intended for things that * are not executed under the kernel (proms, stand alones, kernels, etc). The * format can be executed under the kernel but may demand paged it and not * preload it before execution. * * A core file is in MH_CORE format and can be any in an arbritray legal * Mach-O file. */ // Values for `MachHeader*::filetype`. /// relocatable object file pub const MH_OBJECT: u32 = 0x1; /// demand paged executable file pub const MH_EXECUTE: u32 = 0x2; /// fixed VM shared library file pub const MH_FVMLIB: u32 = 0x3; /// core file pub const MH_CORE: u32 = 0x4; /// preloaded executable file pub const MH_PRELOAD: u32 = 0x5; /// dynamically bound shared library pub const MH_DYLIB: u32 = 0x6; /// dynamic link editor pub const MH_DYLINKER: u32 = 0x7; /// dynamically bound bundle file pub const MH_BUNDLE: u32 = 0x8; /// shared library stub for static linking only, no section contents pub const MH_DYLIB_STUB: u32 = 0x9; /// companion file with only debug sections pub const MH_DSYM: u32 = 0xa; /// x86_64 kexts pub const MH_KEXT_BUNDLE: u32 = 0xb; /// set of mach-o's pub const MH_FILESET: u32 = 0xc; // Values for `MachHeader*::flags`. /// the object file has no undefined references pub const MH_NOUNDEFS: u32 = 0x1; /// the object file is the output of an incremental link against a base file and can't be link edited again pub const MH_INCRLINK: u32 = 0x2; /// the object file is input for the dynamic linker and can't be statically link edited again pub const MH_DYLDLINK: u32 = 0x4; /// the object file's undefined references are bound by the dynamic linker when loaded. pub const MH_BINDATLOAD: u32 = 0x8; /// the file has its dynamic undefined references prebound. pub const MH_PREBOUND: u32 = 0x10; /// the file has its read-only and read-write segments split pub const MH_SPLIT_SEGS: u32 = 0x20; /// the shared library init routine is to be run lazily via catching memory faults to its writeable segments (obsolete) pub const MH_LAZY_INIT: u32 = 0x40; /// the image is using two-level name space bindings pub const MH_TWOLEVEL: u32 = 0x80; /// the executable is forcing all images to use flat name space bindings pub const MH_FORCE_FLAT: u32 = 0x100; /// this umbrella guarantees no multiple definitions of symbols in its sub-images so the two-level namespace hints can always be used. pub const MH_NOMULTIDEFS: u32 = 0x200; /// do not have dyld notify the prebinding agent about this executable pub const MH_NOFIXPREBINDING: u32 = 0x400; /// the binary is not prebound but can have its prebinding redone. only used when MH_PREBOUND is not set. pub const MH_PREBINDABLE: u32 = 0x800; /// indicates that this binary binds to all two-level namespace modules of its dependent libraries. only used when MH_PREBINDABLE and MH_TWOLEVEL are both set. pub const MH_ALLMODSBOUND: u32 = 0x1000; /// safe to divide up the sections into sub-sections via symbols for dead code stripping pub const MH_SUBSECTIONS_VIA_SYMBOLS: u32 = 0x2000; /// the binary has been canonicalized via the unprebind operation pub const MH_CANONICAL: u32 = 0x4000; /// the final linked image contains external weak symbols pub const MH_WEAK_DEFINES: u32 = 0x8000; /// the final linked image uses weak symbols pub const MH_BINDS_TO_WEAK: u32 = 0x10000; /// When this bit is set, all stacks in the task will be given stack execution privilege. Only used in MH_EXECUTE filetypes. pub const MH_ALLOW_STACK_EXECUTION: u32 = 0x20000; /// When this bit is set, the binary declares it is safe for use in processes with uid zero pub const MH_ROOT_SAFE: u32 = 0x40000; /// When this bit is set, the binary declares it is safe for use in processes when issetugid() is true pub const MH_SETUID_SAFE: u32 = 0x80000; /// When this bit is set on a dylib, the static linker does not need to examine dependent dylibs to see if any are re-exported pub const MH_NO_REEXPORTED_DYLIBS: u32 = 0x10_0000; /// When this bit is set, the OS will load the main executable at a random address. Only used in MH_EXECUTE filetypes. pub const MH_PIE: u32 = 0x20_0000; /// Only for use on dylibs. When linking against a dylib that has this bit set, the static linker will automatically not create a LC_LOAD_DYLIB load command to the dylib if no symbols are being referenced from the dylib. pub const MH_DEAD_STRIPPABLE_DYLIB: u32 = 0x40_0000; /// Contains a section of type S_THREAD_LOCAL_VARIABLES pub const MH_HAS_TLV_DESCRIPTORS: u32 = 0x80_0000; /// When this bit is set, the OS will run the main executable with a non-executable heap even on platforms (e.g. i386) that don't require it. Only used in MH_EXECUTE filetypes. pub const MH_NO_HEAP_EXECUTION: u32 = 0x100_0000; /// The code was linked for use in an application extension. pub const MH_APP_EXTENSION_SAFE: u32 = 0x0200_0000; /// The external symbols listed in the nlist symbol table do not include all the symbols listed in the dyld info. pub const MH_NLIST_OUTOFSYNC_WITH_DYLDINFO: u32 = 0x0400_0000; /// Allow LC_MIN_VERSION_MACOS and LC_BUILD_VERSION load commands with /// the platforms macOS, iOSMac, iOSSimulator, tvOSSimulator and watchOSSimulator. pub const MH_SIM_SUPPORT: u32 = 0x0800_0000; /// Only for use on dylibs. When this bit is set, the dylib is part of the dyld /// shared cache, rather than loose in the filesystem. pub const MH_DYLIB_IN_CACHE: u32 = 0x8000_0000; /// Common fields at the start of every load command. /// /// The load commands directly follow the mach_header. The total size of all /// of the commands is given by the sizeofcmds field in the mach_header. All /// load commands must have as their first two fields `cmd` and `cmdsize`. The `cmd` /// field is filled in with a constant for that command type. Each command type /// has a structure specifically for it. The `cmdsize` field is the size in bytes /// of the particular load command structure plus anything that follows it that /// is a part of the load command (i.e. section structures, strings, etc.). To /// advance to the next load command the `cmdsize` can be added to the offset or /// pointer of the current load command. The `cmdsize` for 32-bit architectures /// MUST be a multiple of 4 bytes and for 64-bit architectures MUST be a multiple /// of 8 bytes (these are forever the maximum alignment of any load commands). /// The padded bytes must be zero. All tables in the object file must also /// follow these rules so the file can be memory mapped. Otherwise the pointers /// to these tables will not work well or at all on some machines. With all /// padding zeroed like objects will compare byte for byte. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct LoadCommand { /// Type of load command. /// /// One of the `LC_*` constants. pub cmd: U32, /// Total size of command in bytes. pub cmdsize: U32, } /* * After MacOS X 10.1 when a new load command is added that is required to be * understood by the dynamic linker for the image to execute properly the * LC_REQ_DYLD bit will be or'ed into the load command constant. If the dynamic * linker sees such a load command it it does not understand will issue a * "unknown load command required for execution" error and refuse to use the * image. Other load commands without this bit that are not understood will * simply be ignored. */ pub const LC_REQ_DYLD: u32 = 0x8000_0000; /* Constants for the cmd field of all load commands, the type */ /// segment of this file to be mapped pub const LC_SEGMENT: u32 = 0x1; /// link-edit stab symbol table info pub const LC_SYMTAB: u32 = 0x2; /// link-edit gdb symbol table info (obsolete) pub const LC_SYMSEG: u32 = 0x3; /// thread pub const LC_THREAD: u32 = 0x4; /// unix thread (includes a stack) pub const LC_UNIXTHREAD: u32 = 0x5; /// load a specified fixed VM shared library pub const LC_LOADFVMLIB: u32 = 0x6; /// fixed VM shared library identification pub const LC_IDFVMLIB: u32 = 0x7; /// object identification info (obsolete) pub const LC_IDENT: u32 = 0x8; /// fixed VM file inclusion (internal use) pub const LC_FVMFILE: u32 = 0x9; /// prepage command (internal use) pub const LC_PREPAGE: u32 = 0xa; /// dynamic link-edit symbol table info pub const LC_DYSYMTAB: u32 = 0xb; /// load a dynamically linked shared library pub const LC_LOAD_DYLIB: u32 = 0xc; /// dynamically linked shared lib ident pub const LC_ID_DYLIB: u32 = 0xd; /// load a dynamic linker pub const LC_LOAD_DYLINKER: u32 = 0xe; /// dynamic linker identification pub const LC_ID_DYLINKER: u32 = 0xf; /// modules prebound for a dynamically linked shared library pub const LC_PREBOUND_DYLIB: u32 = 0x10; /// image routines pub const LC_ROUTINES: u32 = 0x11; /// sub framework pub const LC_SUB_FRAMEWORK: u32 = 0x12; /// sub umbrella pub const LC_SUB_UMBRELLA: u32 = 0x13; /// sub client pub const LC_SUB_CLIENT: u32 = 0x14; /// sub library pub const LC_SUB_LIBRARY: u32 = 0x15; /// two-level namespace lookup hints pub const LC_TWOLEVEL_HINTS: u32 = 0x16; /// prebind checksum pub const LC_PREBIND_CKSUM: u32 = 0x17; /// load a dynamically linked shared library that is allowed to be missing /// (all symbols are weak imported). pub const LC_LOAD_WEAK_DYLIB: u32 = 0x18 | LC_REQ_DYLD; /// 64-bit segment of this file to be mapped pub const LC_SEGMENT_64: u32 = 0x19; /// 64-bit image routines pub const LC_ROUTINES_64: u32 = 0x1a; /// the uuid pub const LC_UUID: u32 = 0x1b; /// runpath additions pub const LC_RPATH: u32 = 0x1c | LC_REQ_DYLD; /// local of code signature pub const LC_CODE_SIGNATURE: u32 = 0x1d; /// local of info to split segments pub const LC_SEGMENT_SPLIT_INFO: u32 = 0x1e; /// load and re-export dylib pub const LC_REEXPORT_DYLIB: u32 = 0x1f | LC_REQ_DYLD; /// delay load of dylib until first use pub const LC_LAZY_LOAD_DYLIB: u32 = 0x20; /// encrypted segment information pub const LC_ENCRYPTION_INFO: u32 = 0x21; /// compressed dyld information pub const LC_DYLD_INFO: u32 = 0x22; /// compressed dyld information only pub const LC_DYLD_INFO_ONLY: u32 = 0x22 | LC_REQ_DYLD; /// load upward dylib pub const LC_LOAD_UPWARD_DYLIB: u32 = 0x23 | LC_REQ_DYLD; /// build for MacOSX min OS version pub const LC_VERSION_MIN_MACOSX: u32 = 0x24; /// build for iPhoneOS min OS version pub const LC_VERSION_MIN_IPHONEOS: u32 = 0x25; /// compressed table of function start addresses pub const LC_FUNCTION_STARTS: u32 = 0x26; /// string for dyld to treat like environment variable pub const LC_DYLD_ENVIRONMENT: u32 = 0x27; /// replacement for LC_UNIXTHREAD pub const LC_MAIN: u32 = 0x28 | LC_REQ_DYLD; /// table of non-instructions in __text pub const LC_DATA_IN_CODE: u32 = 0x29; /// source version used to build binary pub const LC_SOURCE_VERSION: u32 = 0x2A; /// Code signing DRs copied from linked dylibs pub const LC_DYLIB_CODE_SIGN_DRS: u32 = 0x2B; /// 64-bit encrypted segment information pub const LC_ENCRYPTION_INFO_64: u32 = 0x2C; /// linker options in MH_OBJECT files pub const LC_LINKER_OPTION: u32 = 0x2D; /// optimization hints in MH_OBJECT files pub const LC_LINKER_OPTIMIZATION_HINT: u32 = 0x2E; /// build for AppleTV min OS version pub const LC_VERSION_MIN_TVOS: u32 = 0x2F; /// build for Watch min OS version pub const LC_VERSION_MIN_WATCHOS: u32 = 0x30; /// arbitrary data included within a Mach-O file pub const LC_NOTE: u32 = 0x31; /// build for platform min OS version pub const LC_BUILD_VERSION: u32 = 0x32; /// used with `LinkeditDataCommand`, payload is trie pub const LC_DYLD_EXPORTS_TRIE: u32 = 0x33 | LC_REQ_DYLD; /// used with `LinkeditDataCommand` pub const LC_DYLD_CHAINED_FIXUPS: u32 = 0x34 | LC_REQ_DYLD; /// used with `FilesetEntryCommand` pub const LC_FILESET_ENTRY: u32 = 0x35 | LC_REQ_DYLD; /// A variable length string in a load command. /// /// The strings are stored just after the load command structure and /// the offset is from the start of the load command structure. The size /// of the string is reflected in the `cmdsize` field of the load command. /// Once again any padded bytes to bring the `cmdsize` field to a multiple /// of 4 bytes must be zero. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct LcStr { /// offset to the string pub offset: U32, } /// 32-bit segment load command. /// /// The segment load command indicates that a part of this file is to be /// mapped into the task's address space. The size of this segment in memory, /// vmsize, maybe equal to or larger than the amount to map from this file, /// filesize. The file is mapped starting at fileoff to the beginning of /// the segment in memory, vmaddr. The rest of the memory of the segment, /// if any, is allocated zero fill on demand. The segment's maximum virtual /// memory protection and initial virtual memory protection are specified /// by the maxprot and initprot fields. If the segment has sections then the /// `Section32` structures directly follow the segment command and their size is /// reflected in `cmdsize`. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct SegmentCommand32 { /// LC_SEGMENT pub cmd: U32, /// includes sizeof section structs pub cmdsize: U32, /// segment name pub segname: [u8; 16], /// memory address of this segment pub vmaddr: U32, /// memory size of this segment pub vmsize: U32, /// file offset of this segment pub fileoff: U32, /// amount to map from the file pub filesize: U32, /// maximum VM protection pub maxprot: U32, /// initial VM protection pub initprot: U32, /// number of sections in segment pub nsects: U32, /// flags pub flags: U32, } /// 64-bit segment load command. /// /// The 64-bit segment load command indicates that a part of this file is to be /// mapped into a 64-bit task's address space. If the 64-bit segment has /// sections then `Section64` structures directly follow the 64-bit segment /// command and their size is reflected in `cmdsize`. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct SegmentCommand64 { /// LC_SEGMENT_64 pub cmd: U32, /// includes sizeof section_64 structs pub cmdsize: U32, /// segment name pub segname: [u8; 16], /// memory address of this segment pub vmaddr: U64, /// memory size of this segment pub vmsize: U64, /// file offset of this segment pub fileoff: U64, /// amount to map from the file pub filesize: U64, /// maximum VM protection pub maxprot: U32, /// initial VM protection pub initprot: U32, /// number of sections in segment pub nsects: U32, /// flags pub flags: U32, } // Values for `SegmentCommand*::flags`. /// the file contents for this segment is for the high part of the VM space, the low part is zero filled (for stacks in core files) pub const SG_HIGHVM: u32 = 0x1; /// this segment is the VM that is allocated by a fixed VM library, for overlap checking in the link editor pub const SG_FVMLIB: u32 = 0x2; /// this segment has nothing that was relocated in it and nothing relocated to it, that is it maybe safely replaced without relocation pub const SG_NORELOC: u32 = 0x4; /// This segment is protected. If the segment starts at file offset 0, the first page of the segment is not protected. All other pages of the segment are protected. pub const SG_PROTECTED_VERSION_1: u32 = 0x8; /// This segment is made read-only after fixups pub const SG_READ_ONLY: u32 = 0x10; /* * A segment is made up of zero or more sections. Non-MH_OBJECT files have * all of their segments with the proper sections in each, and padded to the * specified segment alignment when produced by the link editor. The first * segment of a MH_EXECUTE and MH_FVMLIB format file contains the mach_header * and load commands of the object file before its first section. The zero * fill sections are always last in their segment (in all formats). This * allows the zeroed segment padding to be mapped into memory where zero fill * sections might be. The gigabyte zero fill sections, those with the section * type S_GB_ZEROFILL, can only be in a segment with sections of this type. * These segments are then placed after all other segments. * * The MH_OBJECT format has all of its sections in one segment for * compactness. There is no padding to a specified segment boundary and the * mach_header and load commands are not part of the segment. * * Sections with the same section name, sectname, going into the same segment, * segname, are combined by the link editor. The resulting section is aligned * to the maximum alignment of the combined sections and is the new section's * alignment. The combined sections are aligned to their original alignment in * the combined section. Any padded bytes to get the specified alignment are * zeroed. * * The format of the relocation entries referenced by the reloff and nreloc * fields of the section structure for mach object files is described in the * header file . */ /// 32-bit section. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Section32 { /// name of this section pub sectname: [u8; 16], /// segment this section goes in pub segname: [u8; 16], /// memory address of this section pub addr: U32, /// size in bytes of this section pub size: U32, /// file offset of this section pub offset: U32, /// section alignment (power of 2) pub align: U32, /// file offset of relocation entries pub reloff: U32, /// number of relocation entries pub nreloc: U32, /// flags (section type and attributes) pub flags: U32, /// reserved (for offset or index) pub reserved1: U32, /// reserved (for count or sizeof) pub reserved2: U32, } /// 64-bit section. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Section64 { /// name of this section pub sectname: [u8; 16], /// segment this section goes in pub segname: [u8; 16], /// memory address of this section pub addr: U64, /// size in bytes of this section pub size: U64, /// file offset of this section pub offset: U32, /// section alignment (power of 2) pub align: U32, /// file offset of relocation entries pub reloff: U32, /// number of relocation entries pub nreloc: U32, /// flags (section type and attributes) pub flags: U32, /// reserved (for offset or index) pub reserved1: U32, /// reserved (for count or sizeof) pub reserved2: U32, /// reserved pub reserved3: U32, } /* * The flags field of a section structure is separated into two parts a section * type and section attributes. The section types are mutually exclusive (it * can only have one type) but the section attributes are not (it may have more * than one attribute). */ /// 256 section types pub const SECTION_TYPE: u32 = 0x0000_00ff; /// 24 section attributes pub const SECTION_ATTRIBUTES: u32 = 0xffff_ff00; /* Constants for the type of a section */ /// regular section pub const S_REGULAR: u32 = 0x0; /// zero fill on demand section pub const S_ZEROFILL: u32 = 0x1; /// section with only literal C strings pub const S_CSTRING_LITERALS: u32 = 0x2; /// section with only 4 byte literals pub const S_4BYTE_LITERALS: u32 = 0x3; /// section with only 8 byte literals pub const S_8BYTE_LITERALS: u32 = 0x4; /// section with only pointers to literals pub const S_LITERAL_POINTERS: u32 = 0x5; /* * For the two types of symbol pointers sections and the symbol stubs section * they have indirect symbol table entries. For each of the entries in the * section the indirect symbol table entries, in corresponding order in the * indirect symbol table, start at the index stored in the reserved1 field * of the section structure. Since the indirect symbol table entries * correspond to the entries in the section the number of indirect symbol table * entries is inferred from the size of the section divided by the size of the * entries in the section. For symbol pointers sections the size of the entries * in the section is 4 bytes and for symbol stubs sections the byte size of the * stubs is stored in the reserved2 field of the section structure. */ /// section with only non-lazy symbol pointers pub const S_NON_LAZY_SYMBOL_POINTERS: u32 = 0x6; /// section with only lazy symbol pointers pub const S_LAZY_SYMBOL_POINTERS: u32 = 0x7; /// section with only symbol stubs, byte size of stub in the reserved2 field pub const S_SYMBOL_STUBS: u32 = 0x8; /// section with only function pointers for initialization pub const S_MOD_INIT_FUNC_POINTERS: u32 = 0x9; /// section with only function pointers for termination pub const S_MOD_TERM_FUNC_POINTERS: u32 = 0xa; /// section contains symbols that are to be coalesced pub const S_COALESCED: u32 = 0xb; /// zero fill on demand section (that can be larger than 4 gigabytes) pub const S_GB_ZEROFILL: u32 = 0xc; /// section with only pairs of function pointers for interposing pub const S_INTERPOSING: u32 = 0xd; /// section with only 16 byte literals pub const S_16BYTE_LITERALS: u32 = 0xe; /// section contains DTrace Object Format pub const S_DTRACE_DOF: u32 = 0xf; /// section with only lazy symbol pointers to lazy loaded dylibs pub const S_LAZY_DYLIB_SYMBOL_POINTERS: u32 = 0x10; /* * Section types to support thread local variables */ /// template of initial values for TLVs pub const S_THREAD_LOCAL_REGULAR: u32 = 0x11; /// template of initial values for TLVs pub const S_THREAD_LOCAL_ZEROFILL: u32 = 0x12; /// TLV descriptors pub const S_THREAD_LOCAL_VARIABLES: u32 = 0x13; /// pointers to TLV descriptors pub const S_THREAD_LOCAL_VARIABLE_POINTERS: u32 = 0x14; /// functions to call to initialize TLV values pub const S_THREAD_LOCAL_INIT_FUNCTION_POINTERS: u32 = 0x15; /// 32-bit offsets to initializers pub const S_INIT_FUNC_OFFSETS: u32 = 0x16; /* * Constants for the section attributes part of the flags field of a section * structure. */ /// User setable attributes pub const SECTION_ATTRIBUTES_USR: u32 = 0xff00_0000; /// section contains only true machine instructions pub const S_ATTR_PURE_INSTRUCTIONS: u32 = 0x8000_0000; /// section contains coalesced symbols that are not to be in a ranlib table of contents pub const S_ATTR_NO_TOC: u32 = 0x4000_0000; /// ok to strip static symbols in this section in files with the MH_DYLDLINK flag pub const S_ATTR_STRIP_STATIC_SYMS: u32 = 0x2000_0000; /// no dead stripping pub const S_ATTR_NO_DEAD_STRIP: u32 = 0x1000_0000; /// blocks are live if they reference live blocks pub const S_ATTR_LIVE_SUPPORT: u32 = 0x0800_0000; /// Used with i386 code stubs written on by dyld pub const S_ATTR_SELF_MODIFYING_CODE: u32 = 0x0400_0000; /* * If a segment contains any sections marked with S_ATTR_DEBUG then all * sections in that segment must have this attribute. No section other than * a section marked with this attribute may reference the contents of this * section. A section with this attribute may contain no symbols and must have * a section type S_REGULAR. The static linker will not copy section contents * from sections with this attribute into its output file. These sections * generally contain DWARF debugging info. */ /// a debug section pub const S_ATTR_DEBUG: u32 = 0x0200_0000; /// system setable attributes pub const SECTION_ATTRIBUTES_SYS: u32 = 0x00ff_ff00; /// section contains some machine instructions pub const S_ATTR_SOME_INSTRUCTIONS: u32 = 0x0000_0400; /// section has external relocation entries pub const S_ATTR_EXT_RELOC: u32 = 0x0000_0200; /// section has local relocation entries pub const S_ATTR_LOC_RELOC: u32 = 0x0000_0100; /* * The names of segments and sections in them are mostly meaningless to the * link-editor. But there are few things to support traditional UNIX * executables that require the link-editor and assembler to use some names * agreed upon by convention. * * The initial protection of the "__TEXT" segment has write protection turned * off (not writeable). * * The link-editor will allocate common symbols at the end of the "__common" * section in the "__DATA" segment. It will create the section and segment * if needed. */ /* The currently known segment names and the section names in those segments */ /// the pagezero segment which has no protections and catches NULL references for MH_EXECUTE files pub const SEG_PAGEZERO: &str = "__PAGEZERO"; /// the tradition UNIX text segment pub const SEG_TEXT: &str = "__TEXT"; /// the real text part of the text section no headers, and no padding pub const SECT_TEXT: &str = "__text"; /// the fvmlib initialization section pub const SECT_FVMLIB_INIT0: &str = "__fvmlib_init0"; /// the section following the fvmlib initialization section pub const SECT_FVMLIB_INIT1: &str = "__fvmlib_init1"; /// the tradition UNIX data segment pub const SEG_DATA: &str = "__DATA"; /// the real initialized data section no padding, no bss overlap pub const SECT_DATA: &str = "__data"; /// the real uninitialized data section no padding pub const SECT_BSS: &str = "__bss"; /// the section common symbols are allocated in by the link editor pub const SECT_COMMON: &str = "__common"; /// objective-C runtime segment pub const SEG_OBJC: &str = "__OBJC"; /// symbol table pub const SECT_OBJC_SYMBOLS: &str = "__symbol_table"; /// module information pub const SECT_OBJC_MODULES: &str = "__module_info"; /// string table pub const SECT_OBJC_STRINGS: &str = "__selector_strs"; /// string table pub const SECT_OBJC_REFS: &str = "__selector_refs"; /// the icon segment pub const SEG_ICON: &str = "__ICON"; /// the icon headers pub const SECT_ICON_HEADER: &str = "__header"; /// the icons in tiff format pub const SECT_ICON_TIFF: &str = "__tiff"; /// the segment containing all structs created and maintained by the link editor. Created with -seglinkedit option to ld(1) for MH_EXECUTE and FVMLIB file types only pub const SEG_LINKEDIT: &str = "__LINKEDIT"; /// the segment overlapping with linkedit containing linking information pub const SEG_LINKINFO: &str = "__LINKINFO"; /// the unix stack segment pub const SEG_UNIXSTACK: &str = "__UNIXSTACK"; /// the segment for the self (dyld) modifying code stubs that has read, write and execute permissions pub const SEG_IMPORT: &str = "__IMPORT"; /* * Fixed virtual memory shared libraries are identified by two things. The * target pathname (the name of the library as found for execution), and the * minor version number. The address of where the headers are loaded is in * header_addr. (THIS IS OBSOLETE and no longer supported). */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Fvmlib { /// library's target pathname pub name: LcStr, /// library's minor version number pub minor_version: U32, /// library's header address pub header_addr: U32, } /* * A fixed virtual shared library (filetype == MH_FVMLIB in the mach header) * contains a `FvmlibCommand` (cmd == LC_IDFVMLIB) to identify the library. * An object that uses a fixed virtual shared library also contains a * `FvmlibCommand` (cmd == LC_LOADFVMLIB) for each library it uses. * (THIS IS OBSOLETE and no longer supported). */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct FvmlibCommand { /// LC_IDFVMLIB or LC_LOADFVMLIB pub cmd: U32, /// includes pathname string pub cmdsize: U32, /// the library identification pub fvmlib: Fvmlib, } /* * Dynamically linked shared libraries are identified by two things. The * pathname (the name of the library as found for execution), and the * compatibility version number. The pathname must match and the compatibility * number in the user of the library must be greater than or equal to the * library being used. The time stamp is used to record the time a library was * built and copied into user so it can be use to determined if the library used * at runtime is exactly the same as used to built the program. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Dylib { /// library's path name pub name: LcStr, /// library's build time stamp pub timestamp: U32, /// library's current version number pub current_version: U32, /// library's compatibility vers number pub compatibility_version: U32, } /* * A dynamically linked shared library (filetype == MH_DYLIB in the mach header) * contains a `DylibCommand` (cmd == LC_ID_DYLIB) to identify the library. * An object that uses a dynamically linked shared library also contains a * `DylibCommand` (cmd == LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB, or * LC_REEXPORT_DYLIB) for each library it uses. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct DylibCommand { /// LC_ID_DYLIB, LC_LOAD_{,WEAK_}DYLIB, LC_REEXPORT_DYLIB pub cmd: U32, /// includes pathname string pub cmdsize: U32, /// the library identification pub dylib: Dylib, } /* * A dynamically linked shared library may be a subframework of an umbrella * framework. If so it will be linked with "-umbrella umbrella_name" where * Where "umbrella_name" is the name of the umbrella framework. A subframework * can only be linked against by its umbrella framework or other subframeworks * that are part of the same umbrella framework. Otherwise the static link * editor produces an error and states to link against the umbrella framework. * The name of the umbrella framework for subframeworks is recorded in the * following structure. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct SubFrameworkCommand { /// LC_SUB_FRAMEWORK pub cmd: U32, /// includes umbrella string pub cmdsize: U32, /// the umbrella framework name pub umbrella: LcStr, } /* * For dynamically linked shared libraries that are subframework of an umbrella * framework they can allow clients other than the umbrella framework or other * subframeworks in the same umbrella framework. To do this the subframework * is built with "-allowable_client client_name" and an LC_SUB_CLIENT load * command is created for each -allowable_client flag. The client_name is * usually a framework name. It can also be a name used for bundles clients * where the bundle is built with "-client_name client_name". */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct SubClientCommand { /// LC_SUB_CLIENT pub cmd: U32, /// includes client string pub cmdsize: U32, /// the client name pub client: LcStr, } /* * A dynamically linked shared library may be a sub_umbrella of an umbrella * framework. If so it will be linked with "-sub_umbrella umbrella_name" where * Where "umbrella_name" is the name of the sub_umbrella framework. When * statically linking when -twolevel_namespace is in effect a twolevel namespace * umbrella framework will only cause its subframeworks and those frameworks * listed as sub_umbrella frameworks to be implicited linked in. Any other * dependent dynamic libraries will not be linked it when -twolevel_namespace * is in effect. The primary library recorded by the static linker when * resolving a symbol in these libraries will be the umbrella framework. * Zero or more sub_umbrella frameworks may be use by an umbrella framework. * The name of a sub_umbrella framework is recorded in the following structure. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct SubUmbrellaCommand { /// LC_SUB_UMBRELLA pub cmd: U32, /// includes sub_umbrella string pub cmdsize: U32, /// the sub_umbrella framework name pub sub_umbrella: LcStr, } /* * A dynamically linked shared library may be a sub_library of another shared * library. If so it will be linked with "-sub_library library_name" where * Where "library_name" is the name of the sub_library shared library. When * statically linking when -twolevel_namespace is in effect a twolevel namespace * shared library will only cause its subframeworks and those frameworks * listed as sub_umbrella frameworks and libraries listed as sub_libraries to * be implicited linked in. Any other dependent dynamic libraries will not be * linked it when -twolevel_namespace is in effect. The primary library * recorded by the static linker when resolving a symbol in these libraries * will be the umbrella framework (or dynamic library). Zero or more sub_library * shared libraries may be use by an umbrella framework or (or dynamic library). * The name of a sub_library framework is recorded in the following structure. * For example /usr/lib/libobjc_profile.A.dylib would be recorded as "libobjc". */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct SubLibraryCommand { /// LC_SUB_LIBRARY pub cmd: U32, /// includes sub_library string pub cmdsize: U32, /// the sub_library name pub sub_library: LcStr, } /* * A program (filetype == MH_EXECUTE) that is * prebound to its dynamic libraries has one of these for each library that * the static linker used in prebinding. It contains a bit vector for the * modules in the library. The bits indicate which modules are bound (1) and * which are not (0) from the library. The bit for module 0 is the low bit * of the first byte. So the bit for the Nth module is: * (linked_modules[N/8] >> N%8) & 1 */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct PreboundDylibCommand { /// LC_PREBOUND_DYLIB pub cmd: U32, /// includes strings pub cmdsize: U32, /// library's path name pub name: LcStr, /// number of modules in library pub nmodules: U32, /// bit vector of linked modules pub linked_modules: LcStr, } /* * A program that uses a dynamic linker contains a `DylinkerCommand` to identify * the name of the dynamic linker (LC_LOAD_DYLINKER). And a dynamic linker * contains a `DylinkerCommand` to identify the dynamic linker (LC_ID_DYLINKER). * A file can have at most one of these. * This struct is also used for the LC_DYLD_ENVIRONMENT load command and * contains string for dyld to treat like environment variable. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct DylinkerCommand { /// LC_ID_DYLINKER, LC_LOAD_DYLINKER or LC_DYLD_ENVIRONMENT pub cmd: U32, /// includes pathname string pub cmdsize: U32, /// dynamic linker's path name pub name: LcStr, } /* * Thread commands contain machine-specific data structures suitable for * use in the thread state primitives. The machine specific data structures * follow the struct `ThreadCommand` as follows. * Each flavor of machine specific data structure is preceded by an uint32_t * constant for the flavor of that data structure, an uint32_t that is the * count of uint32_t's of the size of the state data structure and then * the state data structure follows. This triple may be repeated for many * flavors. The constants for the flavors, counts and state data structure * definitions are expected to be in the header file . * These machine specific data structures sizes must be multiples of * 4 bytes. The `cmdsize` reflects the total size of the `ThreadCommand` * and all of the sizes of the constants for the flavors, counts and state * data structures. * * For executable objects that are unix processes there will be one * `ThreadCommand` (cmd == LC_UNIXTHREAD) created for it by the link-editor. * This is the same as a LC_THREAD, except that a stack is automatically * created (based on the shell's limit for the stack size). Command arguments * and environment variables are copied onto that stack. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ThreadCommand { /// LC_THREAD or LC_UNIXTHREAD pub cmd: U32, /// total size of this command pub cmdsize: U32, /* uint32_t flavor flavor of thread state */ /* uint32_t count count of uint32_t's in thread state */ /* struct XXX_thread_state state thread state for this flavor */ /* ... */ } /* * The routines command contains the address of the dynamic shared library * initialization routine and an index into the module table for the module * that defines the routine. Before any modules are used from the library the * dynamic linker fully binds the module that defines the initialization routine * and then calls it. This gets called before any module initialization * routines (used for C++ static constructors) in the library. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct RoutinesCommand32 { /* for 32-bit architectures */ /// LC_ROUTINES pub cmd: U32, /// total size of this command pub cmdsize: U32, /// address of initialization routine pub init_address: U32, /// index into the module table that the init routine is defined in pub init_module: U32, pub reserved1: U32, pub reserved2: U32, pub reserved3: U32, pub reserved4: U32, pub reserved5: U32, pub reserved6: U32, } /* * The 64-bit routines command. Same use as above. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct RoutinesCommand64 { /* for 64-bit architectures */ /// LC_ROUTINES_64 pub cmd: U32, /// total size of this command pub cmdsize: U32, /// address of initialization routine pub init_address: U64, /// index into the module table that the init routine is defined in pub init_module: U64, pub reserved1: U64, pub reserved2: U64, pub reserved3: U64, pub reserved4: U64, pub reserved5: U64, pub reserved6: U64, } /* * The `SymtabCommand` contains the offsets and sizes of the link-edit 4.3BSD * "stab" style symbol table information as described in the header files * and . */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct SymtabCommand { /// LC_SYMTAB pub cmd: U32, /// sizeof(struct SymtabCommand) pub cmdsize: U32, /// symbol table offset pub symoff: U32, /// number of symbol table entries pub nsyms: U32, /// string table offset pub stroff: U32, /// string table size in bytes pub strsize: U32, } /* * This is the second set of the symbolic information which is used to support * the data structures for the dynamically link editor. * * The original set of symbolic information in the `SymtabCommand` which contains * the symbol and string tables must also be present when this load command is * present. When this load command is present the symbol table is organized * into three groups of symbols: * local symbols (static and debugging symbols) - grouped by module * defined external symbols - grouped by module (sorted by name if not lib) * undefined external symbols (sorted by name if MH_BINDATLOAD is not set, * and in order the were seen by the static * linker if MH_BINDATLOAD is set) * In this load command there are offsets and counts to each of the three groups * of symbols. * * This load command contains a the offsets and sizes of the following new * symbolic information tables: * table of contents * module table * reference symbol table * indirect symbol table * The first three tables above (the table of contents, module table and * reference symbol table) are only present if the file is a dynamically linked * shared library. For executable and object modules, which are files * containing only one module, the information that would be in these three * tables is determined as follows: * table of contents - the defined external symbols are sorted by name * module table - the file contains only one module so everything in the * file is part of the module. * reference symbol table - is the defined and undefined external symbols * * For dynamically linked shared library files this load command also contains * offsets and sizes to the pool of relocation entries for all sections * separated into two groups: * external relocation entries * local relocation entries * For executable and object modules the relocation entries continue to hang * off the section structures. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct DysymtabCommand { /// LC_DYSYMTAB pub cmd: U32, /// sizeof(struct DysymtabCommand) pub cmdsize: U32, /* * The symbols indicated by symoff and nsyms of the LC_SYMTAB load command * are grouped into the following three groups: * local symbols (further grouped by the module they are from) * defined external symbols (further grouped by the module they are from) * undefined symbols * * The local symbols are used only for debugging. The dynamic binding * process may have to use them to indicate to the debugger the local * symbols for a module that is being bound. * * The last two groups are used by the dynamic binding process to do the * binding (indirectly through the module table and the reference symbol * table when this is a dynamically linked shared library file). */ /// index to local symbols pub ilocalsym: U32, /// number of local symbols pub nlocalsym: U32, /// index to externally defined symbols pub iextdefsym: U32, /// number of externally defined symbols pub nextdefsym: U32, /// index to undefined symbols pub iundefsym: U32, /// number of undefined symbols pub nundefsym: U32, /* * For the for the dynamic binding process to find which module a symbol * is defined in the table of contents is used (analogous to the ranlib * structure in an archive) which maps defined external symbols to modules * they are defined in. This exists only in a dynamically linked shared * library file. For executable and object modules the defined external * symbols are sorted by name and is use as the table of contents. */ /// file offset to table of contents pub tocoff: U32, /// number of entries in table of contents pub ntoc: U32, /* * To support dynamic binding of "modules" (whole object files) the symbol * table must reflect the modules that the file was created from. This is * done by having a module table that has indexes and counts into the merged * tables for each module. The module structure that these two entries * refer to is described below. This exists only in a dynamically linked * shared library file. For executable and object modules the file only * contains one module so everything in the file belongs to the module. */ /// file offset to module table pub modtaboff: U32, /// number of module table entries pub nmodtab: U32, /* * To support dynamic module binding the module structure for each module * indicates the external references (defined and undefined) each module * makes. For each module there is an offset and a count into the * reference symbol table for the symbols that the module references. * This exists only in a dynamically linked shared library file. For * executable and object modules the defined external symbols and the * undefined external symbols indicates the external references. */ /// offset to referenced symbol table pub extrefsymoff: U32, /// number of referenced symbol table entries pub nextrefsyms: U32, /* * The sections that contain "symbol pointers" and "routine stubs" have * indexes and (implied counts based on the size of the section and fixed * size of the entry) into the "indirect symbol" table for each pointer * and stub. For every section of these two types the index into the * indirect symbol table is stored in the section header in the field * reserved1. An indirect symbol table entry is simply a 32bit index into * the symbol table to the symbol that the pointer or stub is referring to. * The indirect symbol table is ordered to match the entries in the section. */ /// file offset to the indirect symbol table pub indirectsymoff: U32, /// number of indirect symbol table entries pub nindirectsyms: U32, /* * To support relocating an individual module in a library file quickly the * external relocation entries for each module in the library need to be * accessed efficiently. Since the relocation entries can't be accessed * through the section headers for a library file they are separated into * groups of local and external entries further grouped by module. In this * case the presents of this load command who's extreloff, nextrel, * locreloff and nlocrel fields are non-zero indicates that the relocation * entries of non-merged sections are not referenced through the section * structures (and the reloff and nreloc fields in the section headers are * set to zero). * * Since the relocation entries are not accessed through the section headers * this requires the r_address field to be something other than a section * offset to identify the item to be relocated. In this case r_address is * set to the offset from the vmaddr of the first LC_SEGMENT command. * For MH_SPLIT_SEGS images r_address is set to the the offset from the * vmaddr of the first read-write LC_SEGMENT command. * * The relocation entries are grouped by module and the module table * entries have indexes and counts into them for the group of external * relocation entries for that the module. * * For sections that are merged across modules there must not be any * remaining external relocation entries for them (for merged sections * remaining relocation entries must be local). */ /// offset to external relocation entries pub extreloff: U32, /// number of external relocation entries pub nextrel: U32, /* * All the local relocation entries are grouped together (they are not * grouped by their module since they are only used if the object is moved * from it statically link edited address). */ /// offset to local relocation entries pub locreloff: U32, /// number of local relocation entries pub nlocrel: U32, } /* * An indirect symbol table entry is simply a 32bit index into the symbol table * to the symbol that the pointer or stub is referring to. Unless it is for a * non-lazy symbol pointer section for a defined symbol which strip(1) as * removed. In which case it has the value INDIRECT_SYMBOL_LOCAL. If the * symbol was also absolute INDIRECT_SYMBOL_ABS is or'ed with that. */ pub const INDIRECT_SYMBOL_LOCAL: u32 = 0x8000_0000; pub const INDIRECT_SYMBOL_ABS: u32 = 0x4000_0000; /* a table of contents entry */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct DylibTableOfContents { /// the defined external symbol (index into the symbol table) pub symbol_index: U32, /// index into the module table this symbol is defined in pub module_index: U32, } /* a module table entry */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct DylibModule32 { /// the module name (index into string table) pub module_name: U32, /// index into externally defined symbols pub iextdefsym: U32, /// number of externally defined symbols pub nextdefsym: U32, /// index into reference symbol table pub irefsym: U32, /// number of reference symbol table entries pub nrefsym: U32, /// index into symbols for local symbols pub ilocalsym: U32, /// number of local symbols pub nlocalsym: U32, /// index into external relocation entries pub iextrel: U32, /// number of external relocation entries pub nextrel: U32, /// low 16 bits are the index into the init section, high 16 bits are the index into the term section pub iinit_iterm: U32, /// low 16 bits are the number of init section entries, high 16 bits are the number of term section entries pub ninit_nterm: U32, /// for this module address of the start of the (__OBJC,__module_info) section pub objc_module_info_addr: U32, /// for this module size of the (__OBJC,__module_info) section pub objc_module_info_size: U32, } /* a 64-bit module table entry */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct DylibModule64 { /// the module name (index into string table) pub module_name: U32, /// index into externally defined symbols pub iextdefsym: U32, /// number of externally defined symbols pub nextdefsym: U32, /// index into reference symbol table pub irefsym: U32, /// number of reference symbol table entries pub nrefsym: U32, /// index into symbols for local symbols pub ilocalsym: U32, /// number of local symbols pub nlocalsym: U32, /// index into external relocation entries pub iextrel: U32, /// number of external relocation entries pub nextrel: U32, /// low 16 bits are the index into the init section, high 16 bits are the index into the term section pub iinit_iterm: U32, /// low 16 bits are the number of init section entries, high 16 bits are the number of term section entries pub ninit_nterm: U32, /// for this module size of the (__OBJC,__module_info) section pub objc_module_info_size: U32, /// for this module address of the start of the (__OBJC,__module_info) section pub objc_module_info_addr: U64, } /* * The entries in the reference symbol table are used when loading the module * (both by the static and dynamic link editors) and if the module is unloaded * or replaced. Therefore all external symbols (defined and undefined) are * listed in the module's reference table. The flags describe the type of * reference that is being made. The constants for the flags are defined in * as they are also used for symbol table entries. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct DylibReference { /* TODO: uint32_t isym:24, /* index into the symbol table */ flags:8; /* flags to indicate the type of reference */ */ pub bitfield: U32, } /* * The TwolevelHintsCommand contains the offset and number of hints in the * two-level namespace lookup hints table. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct TwolevelHintsCommand { /// LC_TWOLEVEL_HINTS pub cmd: U32, /// sizeof(struct TwolevelHintsCommand) pub cmdsize: U32, /// offset to the hint table pub offset: U32, /// number of hints in the hint table pub nhints: U32, } /* * The entries in the two-level namespace lookup hints table are TwolevelHint * structs. These provide hints to the dynamic link editor where to start * looking for an undefined symbol in a two-level namespace image. The * isub_image field is an index into the sub-images (sub-frameworks and * sub-umbrellas list) that made up the two-level image that the undefined * symbol was found in when it was built by the static link editor. If * isub-image is 0 the the symbol is expected to be defined in library and not * in the sub-images. If isub-image is non-zero it is an index into the array * of sub-images for the umbrella with the first index in the sub-images being * 1. The array of sub-images is the ordered list of sub-images of the umbrella * that would be searched for a symbol that has the umbrella recorded as its * primary library. The table of contents index is an index into the * library's table of contents. This is used as the starting point of the * binary search or a directed linear search. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct TwolevelHint { /* TODO: uint32_t isub_image:8, /* index into the sub images */ itoc:24; /* index into the table of contents */ */ pub bitfield: U32, } /* * The PrebindCksumCommand contains the value of the original check sum for * prebound files or zero. When a prebound file is first created or modified * for other than updating its prebinding information the value of the check sum * is set to zero. When the file has it prebinding re-done and if the value of * the check sum is zero the original check sum is calculated and stored in * cksum field of this load command in the output file. If when the prebinding * is re-done and the cksum field is non-zero it is left unchanged from the * input file. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct PrebindCksumCommand { /// LC_PREBIND_CKSUM pub cmd: U32, /// sizeof(struct PrebindCksumCommand) pub cmdsize: U32, /// the check sum or zero pub cksum: U32, } /* * The uuid load command contains a single 128-bit unique random number that * identifies an object produced by the static link editor. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct UuidCommand { /// LC_UUID pub cmd: U32, /// sizeof(struct UuidCommand) pub cmdsize: U32, /// the 128-bit uuid pub uuid: [u8; 16], } /* * The RpathCommand contains a path which at runtime should be added to * the current run path used to find @rpath prefixed dylibs. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct RpathCommand { /// LC_RPATH pub cmd: U32, /// includes string pub cmdsize: U32, /// path to add to run path pub path: LcStr, } /* * The LinkeditDataCommand contains the offsets and sizes of a blob * of data in the __LINKEDIT segment. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct LinkeditDataCommand { /// `LC_CODE_SIGNATURE`, `LC_SEGMENT_SPLIT_INFO`, `LC_FUNCTION_STARTS`, /// `LC_DATA_IN_CODE`, `LC_DYLIB_CODE_SIGN_DRS`, `LC_LINKER_OPTIMIZATION_HINT`, /// `LC_DYLD_EXPORTS_TRIE`, or `LC_DYLD_CHAINED_FIXUPS`. pub cmd: U32, /// sizeof(struct LinkeditDataCommand) pub cmdsize: U32, /// file offset of data in __LINKEDIT segment pub dataoff: U32, /// file size of data in __LINKEDIT segment pub datasize: U32, } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct FilesetEntryCommand { // LC_FILESET_ENTRY pub cmd: U32, /// includes id string pub cmdsize: U32, /// memory address of the dylib pub vmaddr: U64, /// file offset of the dylib pub fileoff: U64, /// contained entry id pub entry_id: LcStr, /// entry_id is 32-bits long, so this is the reserved padding pub reserved: U32, } /* * The EncryptionInfoCommand32 contains the file offset and size of an * of an encrypted segment. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct EncryptionInfoCommand32 { /// LC_ENCRYPTION_INFO pub cmd: U32, /// sizeof(struct EncryptionInfoCommand32) pub cmdsize: U32, /// file offset of encrypted range pub cryptoff: U32, /// file size of encrypted range pub cryptsize: U32, /// which enryption system, 0 means not-encrypted yet pub cryptid: U32, } /* * The EncryptionInfoCommand64 contains the file offset and size of an * of an encrypted segment (for use in x86_64 targets). */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct EncryptionInfoCommand64 { /// LC_ENCRYPTION_INFO_64 pub cmd: U32, /// sizeof(struct EncryptionInfoCommand64) pub cmdsize: U32, /// file offset of encrypted range pub cryptoff: U32, /// file size of encrypted range pub cryptsize: U32, /// which enryption system, 0 means not-encrypted yet pub cryptid: U32, /// padding to make this struct's size a multiple of 8 bytes pub pad: U32, } /* * The VersionMinCommand contains the min OS version on which this * binary was built to run. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct VersionMinCommand { /// LC_VERSION_MIN_MACOSX or LC_VERSION_MIN_IPHONEOS or LC_VERSION_MIN_WATCHOS or LC_VERSION_MIN_TVOS pub cmd: U32, /// sizeof(struct VersionMinCommand) pub cmdsize: U32, /// X.Y.Z is encoded in nibbles xxxx.yy.zz pub version: U32, /// X.Y.Z is encoded in nibbles xxxx.yy.zz pub sdk: U32, } /* * The BuildVersionCommand contains the min OS version on which this * binary was built to run for its platform. The list of known platforms and * tool values following it. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct BuildVersionCommand { /// LC_BUILD_VERSION pub cmd: U32, /// sizeof(struct BuildVersionCommand) plus ntools * sizeof(struct BuildToolVersion) pub cmdsize: U32, /// platform pub platform: U32, /// X.Y.Z is encoded in nibbles xxxx.yy.zz pub minos: U32, /// X.Y.Z is encoded in nibbles xxxx.yy.zz pub sdk: U32, /// number of tool entries following this pub ntools: U32, } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct BuildToolVersion { /// enum for the tool pub tool: U32, /// version number of the tool pub version: U32, } /* Known values for the platform field above. */ pub const PLATFORM_MACOS: u32 = 1; pub const PLATFORM_IOS: u32 = 2; pub const PLATFORM_TVOS: u32 = 3; pub const PLATFORM_WATCHOS: u32 = 4; pub const PLATFORM_BRIDGEOS: u32 = 5; pub const PLATFORM_MACCATALYST: u32 = 6; pub const PLATFORM_IOSSIMULATOR: u32 = 7; pub const PLATFORM_TVOSSIMULATOR: u32 = 8; pub const PLATFORM_WATCHOSSIMULATOR: u32 = 9; pub const PLATFORM_DRIVERKIT: u32 = 10; pub const PLATFORM_XROS: u32 = 11; pub const PLATFORM_XROSSIMULATOR: u32 = 12; /* Known values for the tool field above. */ pub const TOOL_CLANG: u32 = 1; pub const TOOL_SWIFT: u32 = 2; pub const TOOL_LD: u32 = 3; /* * The DyldInfoCommand contains the file offsets and sizes of * the new compressed form of the information dyld needs to * load the image. This information is used by dyld on Mac OS X * 10.6 and later. All information pointed to by this command * is encoded using byte streams, so no endian swapping is needed * to interpret it. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct DyldInfoCommand { /// LC_DYLD_INFO or LC_DYLD_INFO_ONLY pub cmd: U32, /// sizeof(struct DyldInfoCommand) pub cmdsize: U32, /* * Dyld rebases an image whenever dyld loads it at an address different * from its preferred address. The rebase information is a stream * of byte sized opcodes whose symbolic names start with REBASE_OPCODE_. * Conceptually the rebase information is a table of tuples: * * The opcodes are a compressed way to encode the table by only * encoding when a column changes. In addition simple patterns * like "every n'th offset for m times" can be encoded in a few * bytes. */ /// file offset to rebase info pub rebase_off: U32, /// size of rebase info pub rebase_size: U32, /* * Dyld binds an image during the loading process, if the image * requires any pointers to be initialized to symbols in other images. * The bind information is a stream of byte sized * opcodes whose symbolic names start with BIND_OPCODE_. * Conceptually the bind information is a table of tuples: * * The opcodes are a compressed way to encode the table by only * encoding when a column changes. In addition simple patterns * like for runs of pointers initialized to the same value can be * encoded in a few bytes. */ /// file offset to binding info pub bind_off: U32, /// size of binding info pub bind_size: U32, /* * Some C++ programs require dyld to unique symbols so that all * images in the process use the same copy of some code/data. * This step is done after binding. The content of the weak_bind * info is an opcode stream like the bind_info. But it is sorted * alphabetically by symbol name. This enable dyld to walk * all images with weak binding information in order and look * for collisions. If there are no collisions, dyld does * no updating. That means that some fixups are also encoded * in the bind_info. For instance, all calls to "operator new" * are first bound to libstdc++.dylib using the information * in bind_info. Then if some image overrides operator new * that is detected when the weak_bind information is processed * and the call to operator new is then rebound. */ /// file offset to weak binding info pub weak_bind_off: U32, /// size of weak binding info pub weak_bind_size: U32, /* * Some uses of external symbols do not need to be bound immediately. * Instead they can be lazily bound on first use. The lazy_bind * are contains a stream of BIND opcodes to bind all lazy symbols. * Normal use is that dyld ignores the lazy_bind section when * loading an image. Instead the static linker arranged for the * lazy pointer to initially point to a helper function which * pushes the offset into the lazy_bind area for the symbol * needing to be bound, then jumps to dyld which simply adds * the offset to lazy_bind_off to get the information on what * to bind. */ /// file offset to lazy binding info pub lazy_bind_off: U32, /// size of lazy binding infs pub lazy_bind_size: U32, /* * The symbols exported by a dylib are encoded in a trie. This * is a compact representation that factors out common prefixes. * It also reduces LINKEDIT pages in RAM because it encodes all * information (name, address, flags) in one small, contiguous range. * The export area is a stream of nodes. The first node sequentially * is the start node for the trie. * * Nodes for a symbol start with a uleb128 that is the length of * the exported symbol information for the string so far. * If there is no exported symbol, the node starts with a zero byte. * If there is exported info, it follows the length. * * First is a uleb128 containing flags. Normally, it is followed by * a uleb128 encoded offset which is location of the content named * by the symbol from the mach_header for the image. If the flags * is EXPORT_SYMBOL_FLAGS_REEXPORT, then following the flags is * a uleb128 encoded library ordinal, then a zero terminated * UTF8 string. If the string is zero length, then the symbol * is re-export from the specified dylib with the same name. * If the flags is EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER, then following * the flags is two uleb128s: the stub offset and the resolver offset. * The stub is used by non-lazy pointers. The resolver is used * by lazy pointers and must be called to get the actual address to use. * * After the optional exported symbol information is a byte of * how many edges (0-255) that this node has leaving it, * followed by each edge. * Each edge is a zero terminated UTF8 of the addition chars * in the symbol, followed by a uleb128 offset for the node that * edge points to. * */ /// file offset to lazy binding info pub export_off: U32, /// size of lazy binding infs pub export_size: U32, } /* * The following are used to encode rebasing information */ pub const REBASE_TYPE_POINTER: u8 = 1; pub const REBASE_TYPE_TEXT_ABSOLUTE32: u8 = 2; pub const REBASE_TYPE_TEXT_PCREL32: u8 = 3; pub const REBASE_OPCODE_MASK: u8 = 0xF0; pub const REBASE_IMMEDIATE_MASK: u8 = 0x0F; pub const REBASE_OPCODE_DONE: u8 = 0x00; pub const REBASE_OPCODE_SET_TYPE_IMM: u8 = 0x10; pub const REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: u8 = 0x20; pub const REBASE_OPCODE_ADD_ADDR_ULEB: u8 = 0x30; pub const REBASE_OPCODE_ADD_ADDR_IMM_SCALED: u8 = 0x40; pub const REBASE_OPCODE_DO_REBASE_IMM_TIMES: u8 = 0x50; pub const REBASE_OPCODE_DO_REBASE_ULEB_TIMES: u8 = 0x60; pub const REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: u8 = 0x70; pub const REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: u8 = 0x80; /* * The following are used to encode binding information */ pub const BIND_TYPE_POINTER: u8 = 1; pub const BIND_TYPE_TEXT_ABSOLUTE32: u8 = 2; pub const BIND_TYPE_TEXT_PCREL32: u8 = 3; pub const BIND_SPECIAL_DYLIB_SELF: i8 = 0; pub const BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE: i8 = -1; pub const BIND_SPECIAL_DYLIB_FLAT_LOOKUP: i8 = -2; pub const BIND_SPECIAL_DYLIB_WEAK_LOOKUP: i8 = -3; pub const BIND_SYMBOL_FLAGS_WEAK_IMPORT: u8 = 0x1; pub const BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION: u8 = 0x8; pub const BIND_OPCODE_MASK: u8 = 0xF0; pub const BIND_IMMEDIATE_MASK: u8 = 0x0F; pub const BIND_OPCODE_DONE: u8 = 0x00; pub const BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: u8 = 0x10; pub const BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: u8 = 0x20; pub const BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: u8 = 0x30; pub const BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: u8 = 0x40; pub const BIND_OPCODE_SET_TYPE_IMM: u8 = 0x50; pub const BIND_OPCODE_SET_ADDEND_SLEB: u8 = 0x60; pub const BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: u8 = 0x70; pub const BIND_OPCODE_ADD_ADDR_ULEB: u8 = 0x80; pub const BIND_OPCODE_DO_BIND: u8 = 0x90; pub const BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: u8 = 0xA0; pub const BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: u8 = 0xB0; pub const BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: u8 = 0xC0; pub const BIND_OPCODE_THREADED: u8 = 0xD0; pub const BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB: u8 = 0x00; pub const BIND_SUBOPCODE_THREADED_APPLY: u8 = 0x01; /* * The following are used on the flags byte of a terminal node * in the export information. */ pub const EXPORT_SYMBOL_FLAGS_KIND_MASK: u32 = 0x03; pub const EXPORT_SYMBOL_FLAGS_KIND_REGULAR: u32 = 0x00; pub const EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: u32 = 0x01; pub const EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: u32 = 0x02; pub const EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION: u32 = 0x04; pub const EXPORT_SYMBOL_FLAGS_REEXPORT: u32 = 0x08; pub const EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER: u32 = 0x10; /* * The LinkerOptionCommand contains linker options embedded in object files. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct LinkerOptionCommand { /// LC_LINKER_OPTION only used in MH_OBJECT filetypes pub cmd: U32, pub cmdsize: U32, /// number of strings pub count: U32, /* concatenation of zero terminated UTF8 strings. Zero filled at end to align */ } /* * The SymsegCommand contains the offset and size of the GNU style * symbol table information as described in the header file . * The symbol roots of the symbol segments must also be aligned properly * in the file. So the requirement of keeping the offsets aligned to a * multiple of a 4 bytes translates to the length field of the symbol * roots also being a multiple of a long. Also the padding must again be * zeroed. (THIS IS OBSOLETE and no longer supported). */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct SymsegCommand { /// LC_SYMSEG pub cmd: U32, /// sizeof(struct SymsegCommand) pub cmdsize: U32, /// symbol segment offset pub offset: U32, /// symbol segment size in bytes pub size: U32, } /* * The IdentCommand contains a free format string table following the * IdentCommand structure. The strings are null terminated and the size of * the command is padded out with zero bytes to a multiple of 4 bytes/ * (THIS IS OBSOLETE and no longer supported). */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct IdentCommand { /// LC_IDENT pub cmd: U32, /// strings that follow this command pub cmdsize: U32, } /* * The FvmfileCommand contains a reference to a file to be loaded at the * specified virtual address. (Presently, this command is reserved for * internal use. The kernel ignores this command when loading a program into * memory). */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct FvmfileCommand { /// LC_FVMFILE pub cmd: U32, /// includes pathname string pub cmdsize: U32, /// files pathname pub name: LcStr, /// files virtual address pub header_addr: U32, } /* * The EntryPointCommand is a replacement for thread_command. * It is used for main executables to specify the location (file offset) * of main(). If -stack_size was used at link time, the stacksize * field will contain the stack size need for the main thread. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct EntryPointCommand { /// LC_MAIN only used in MH_EXECUTE filetypes pub cmd: U32, /// 24 pub cmdsize: U32, /// file (__TEXT) offset of main() pub entryoff: U64, /// if not zero, initial stack size pub stacksize: U64, } /* * The SourceVersionCommand is an optional load command containing * the version of the sources used to build the binary. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct SourceVersionCommand { /// LC_SOURCE_VERSION pub cmd: U32, /// 16 pub cmdsize: U32, /// A.B.C.D.E packed as a24.b10.c10.d10.e10 pub version: U64, } /* * The LC_DATA_IN_CODE load commands uses a LinkeditDataCommand * to point to an array of DataInCodeEntry entries. Each entry * describes a range of data in a code section. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct DataInCodeEntry { /// from mach_header to start of data range pub offset: U32, /// number of bytes in data range pub length: U16, /// a DICE_KIND_* value pub kind: U16, } pub const DICE_KIND_DATA: u32 = 0x0001; pub const DICE_KIND_JUMP_TABLE8: u32 = 0x0002; pub const DICE_KIND_JUMP_TABLE16: u32 = 0x0003; pub const DICE_KIND_JUMP_TABLE32: u32 = 0x0004; pub const DICE_KIND_ABS_JUMP_TABLE32: u32 = 0x0005; /* * Sections of type S_THREAD_LOCAL_VARIABLES contain an array * of TlvDescriptor structures. */ /* TODO: #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct TlvDescriptor { void* (*thunk)(struct TlvDescriptor*); unsigned long key; unsigned long offset; } */ /* * LC_NOTE commands describe a region of arbitrary data included in a Mach-O * file. Its initial use is to record extra data in MH_CORE files. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct NoteCommand { /// LC_NOTE pub cmd: U32, /// sizeof(struct NoteCommand) pub cmdsize: U32, /// owner name for this LC_NOTE pub data_owner: [u8; 16], /// file offset of this data pub offset: U64, /// length of data region pub size: U64, } // Definitions from "/usr/include/mach-o/nlist.h". #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Nlist32 { /// index into the string table pub n_strx: U32, /// type flag, see below pub n_type: u8, /// section number or NO_SECT pub n_sect: u8, /// see pub n_desc: U16, /// value of this symbol (or stab offset) pub n_value: U32, } /* * This is the symbol table entry structure for 64-bit architectures. */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Nlist64 { /// index into the string table pub n_strx: U32, /// type flag, see below pub n_type: u8, /// section number or NO_SECT pub n_sect: u8, /// see pub n_desc: U16, /// value of this symbol (or stab offset) // Note: 4 byte alignment has been observed in practice. pub n_value: U64Bytes, } /* * Symbols with a index into the string table of zero (n_un.n_strx == 0) are * defined to have a null, "", name. Therefore all string indexes to non null * names must not have a zero string index. This is bit historical information * that has never been well documented. */ /* * The n_type field really contains four fields: * unsigned char N_STAB:3, * N_PEXT:1, * N_TYPE:3, * N_EXT:1; * which are used via the following masks. */ /// if any of these bits set, a symbolic debugging entry pub const N_STAB: u8 = 0xe0; /// private external symbol bit pub const N_PEXT: u8 = 0x10; /// mask for the type bits pub const N_TYPE: u8 = 0x0e; /// external symbol bit, set for external symbols pub const N_EXT: u8 = 0x01; /* * Only symbolic debugging entries have some of the N_STAB bits set and if any * of these bits are set then it is a symbolic debugging entry (a stab). In * which case then the values of the n_type field (the entire field) are given * in */ /* * Values for N_TYPE bits of the n_type field. */ /// undefined, n_sect == NO_SECT pub const N_UNDF: u8 = 0x0; /// absolute, n_sect == NO_SECT pub const N_ABS: u8 = 0x2; /// defined in section number n_sect pub const N_SECT: u8 = 0xe; /// prebound undefined (defined in a dylib) pub const N_PBUD: u8 = 0xc; /// indirect pub const N_INDR: u8 = 0xa; /* * If the type is N_INDR then the symbol is defined to be the same as another * symbol. In this case the n_value field is an index into the string table * of the other symbol's name. When the other symbol is defined then they both * take on the defined type and value. */ /* * If the type is N_SECT then the n_sect field contains an ordinal of the * section the symbol is defined in. The sections are numbered from 1 and * refer to sections in order they appear in the load commands for the file * they are in. This means the same ordinal may very well refer to different * sections in different files. * * The n_value field for all symbol table entries (including N_STAB's) gets * updated by the link editor based on the value of it's n_sect field and where * the section n_sect references gets relocated. If the value of the n_sect * field is NO_SECT then it's n_value field is not changed by the link editor. */ /// symbol is not in any section pub const NO_SECT: u8 = 0; /// 1 thru 255 inclusive pub const MAX_SECT: u8 = 255; /* * Common symbols are represented by undefined (N_UNDF) external (N_EXT) types * who's values (n_value) are non-zero. In which case the value of the n_value * field is the size (in bytes) of the common symbol. The n_sect field is set * to NO_SECT. The alignment of a common symbol may be set as a power of 2 * between 2^1 and 2^15 as part of the n_desc field using the macros below. If * the alignment is not set (a value of zero) then natural alignment based on * the size is used. */ /* TODO: #define GET_COMM_ALIGN(n_desc) (((n_desc) >> 8) & 0x0f) #define SET_COMM_ALIGN(n_desc,align) \ (n_desc) = (((n_desc) & 0xf0ff) | (((align) & 0x0f) << 8)) */ /* * To support the lazy binding of undefined symbols in the dynamic link-editor, * the undefined symbols in the symbol table (the nlist structures) are marked * with the indication if the undefined reference is a lazy reference or * non-lazy reference. If both a non-lazy reference and a lazy reference is * made to the same symbol the non-lazy reference takes precedence. A reference * is lazy only when all references to that symbol are made through a symbol * pointer in a lazy symbol pointer section. * * The implementation of marking nlist structures in the symbol table for * undefined symbols will be to use some of the bits of the n_desc field as a * reference type. The mask REFERENCE_TYPE will be applied to the n_desc field * of an nlist structure for an undefined symbol to determine the type of * undefined reference (lazy or non-lazy). * * The constants for the REFERENCE FLAGS are propagated to the reference table * in a shared library file. In that case the constant for a defined symbol, * REFERENCE_FLAG_DEFINED, is also used. */ /* Reference type bits of the n_desc field of undefined symbols */ pub const REFERENCE_TYPE: u16 = 0x7; /* types of references */ pub const REFERENCE_FLAG_UNDEFINED_NON_LAZY: u16 = 0; pub const REFERENCE_FLAG_UNDEFINED_LAZY: u16 = 1; pub const REFERENCE_FLAG_DEFINED: u16 = 2; pub const REFERENCE_FLAG_PRIVATE_DEFINED: u16 = 3; pub const REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY: u16 = 4; pub const REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY: u16 = 5; /* * To simplify stripping of objects that use are used with the dynamic link * editor, the static link editor marks the symbols defined an object that are * referenced by a dynamically bound object (dynamic shared libraries, bundles). * With this marking strip knows not to strip these symbols. */ pub const REFERENCED_DYNAMICALLY: u16 = 0x0010; /* * For images created by the static link editor with the -twolevel_namespace * option in effect the flags field of the mach header is marked with * MH_TWOLEVEL. And the binding of the undefined references of the image are * determined by the static link editor. Which library an undefined symbol is * bound to is recorded by the static linker in the high 8 bits of the n_desc * field using the SET_LIBRARY_ORDINAL macro below. The ordinal recorded * references the libraries listed in the Mach-O's LC_LOAD_DYLIB, * LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIB, LC_LOAD_UPWARD_DYLIB, and * LC_LAZY_LOAD_DYLIB, etc. load commands in the order they appear in the * headers. The library ordinals start from 1. * For a dynamic library that is built as a two-level namespace image the * undefined references from module defined in another use the same nlist struct * an in that case SELF_LIBRARY_ORDINAL is used as the library ordinal. For * defined symbols in all images they also must have the library ordinal set to * SELF_LIBRARY_ORDINAL. The EXECUTABLE_ORDINAL refers to the executable * image for references from plugins that refer to the executable that loads * them. * * The DYNAMIC_LOOKUP_ORDINAL is for undefined symbols in a two-level namespace * image that are looked up by the dynamic linker with flat namespace semantics. * This ordinal was added as a feature in Mac OS X 10.3 by reducing the * value of MAX_LIBRARY_ORDINAL by one. So it is legal for existing binaries * or binaries built with older tools to have 0xfe (254) dynamic libraries. In * this case the ordinal value 0xfe (254) must be treated as a library ordinal * for compatibility. */ /* TODO: #define GET_LIBRARY_ORDINAL(n_desc) (((n_desc) >> 8) & 0xff) #define SET_LIBRARY_ORDINAL(n_desc,ordinal) \ (n_desc) = (((n_desc) & 0x00ff) | (((ordinal) & 0xff) << 8)) */ pub const SELF_LIBRARY_ORDINAL: u8 = 0x0; pub const MAX_LIBRARY_ORDINAL: u8 = 0xfd; pub const DYNAMIC_LOOKUP_ORDINAL: u8 = 0xfe; pub const EXECUTABLE_ORDINAL: u8 = 0xff; /* * The bit 0x0020 of the n_desc field is used for two non-overlapping purposes * and has two different symbolic names, N_NO_DEAD_STRIP and N_DESC_DISCARDED. */ /* * The N_NO_DEAD_STRIP bit of the n_desc field only ever appears in a * relocatable .o file (MH_OBJECT filetype). And is used to indicate to the * static link editor it is never to dead strip the symbol. */ /// symbol is not to be dead stripped pub const N_NO_DEAD_STRIP: u16 = 0x0020; /* * The N_DESC_DISCARDED bit of the n_desc field never appears in linked image. * But is used in very rare cases by the dynamic link editor to mark an in * memory symbol as discared and longer used for linking. */ /// symbol is discarded pub const N_DESC_DISCARDED: u16 = 0x0020; /* * The N_WEAK_REF bit of the n_desc field indicates to the dynamic linker that * the undefined symbol is allowed to be missing and is to have the address of * zero when missing. */ /// symbol is weak referenced pub const N_WEAK_REF: u16 = 0x0040; /* * The N_WEAK_DEF bit of the n_desc field indicates to the static and dynamic * linkers that the symbol definition is weak, allowing a non-weak symbol to * also be used which causes the weak definition to be discared. Currently this * is only supported for symbols in coalesced sections. */ /// coalesced symbol is a weak definition pub const N_WEAK_DEF: u16 = 0x0080; /* * The N_REF_TO_WEAK bit of the n_desc field indicates to the dynamic linker * that the undefined symbol should be resolved using flat namespace searching. */ /// reference to a weak symbol pub const N_REF_TO_WEAK: u16 = 0x0080; /* * The N_ARM_THUMB_DEF bit of the n_desc field indicates that the symbol is * a definition of a Thumb function. */ /// symbol is a Thumb function (ARM) pub const N_ARM_THUMB_DEF: u16 = 0x0008; /* * The N_SYMBOL_RESOLVER bit of the n_desc field indicates that the * that the function is actually a resolver function and should * be called to get the address of the real function to use. * This bit is only available in .o files (MH_OBJECT filetype) */ pub const N_SYMBOL_RESOLVER: u16 = 0x0100; /* * The N_ALT_ENTRY bit of the n_desc field indicates that the * symbol is pinned to the previous content. */ pub const N_ALT_ENTRY: u16 = 0x0200; // Definitions from "/usr/include/mach-o/stab.h". /* * This file gives definitions supplementing for permanent symbol * table entries of Mach-O files. Modified from the BSD definitions. The * modifications from the original definitions were changing what the values of * what was the n_other field (an unused field) which is now the n_sect field. * These modifications are required to support symbols in an arbitrary number of * sections not just the three sections (text, data and bss) in a BSD file. * The values of the defined constants have NOT been changed. * * These must have one of the N_STAB bits on. The n_value fields are subject * to relocation according to the value of their n_sect field. So for types * that refer to things in sections the n_sect field must be filled in with the * proper section ordinal. For types that are not to have their n_value field * relocatated the n_sect field must be NO_SECT. */ /* * Symbolic debugger symbols. The comments give the conventional use for * * .stabs "n_name", n_type, n_sect, n_desc, n_value * * where n_type is the defined constant and not listed in the comment. Other * fields not listed are zero. n_sect is the section ordinal the entry is * referring to. */ /// global symbol: name,,NO_SECT,type,0 pub const N_GSYM: u8 = 0x20; /// procedure name (f77 kludge): name,,NO_SECT,0,0 pub const N_FNAME: u8 = 0x22; /// procedure: name,,n_sect,linenumber,address pub const N_FUN: u8 = 0x24; /// static symbol: name,,n_sect,type,address pub const N_STSYM: u8 = 0x26; /// .lcomm symbol: name,,n_sect,type,address pub const N_LCSYM: u8 = 0x28; /// begin nsect sym: 0,,n_sect,0,address pub const N_BNSYM: u8 = 0x2e; /// AST file path: name,,NO_SECT,0,0 pub const N_AST: u8 = 0x32; /// emitted with gcc2_compiled and in gcc source pub const N_OPT: u8 = 0x3c; /// register sym: name,,NO_SECT,type,register pub const N_RSYM: u8 = 0x40; /// src line: 0,,n_sect,linenumber,address pub const N_SLINE: u8 = 0x44; /// end nsect sym: 0,,n_sect,0,address pub const N_ENSYM: u8 = 0x4e; /// structure elt: name,,NO_SECT,type,struct_offset pub const N_SSYM: u8 = 0x60; /// source file name: name,,n_sect,0,address pub const N_SO: u8 = 0x64; /// object file name: name,,0,0,st_mtime pub const N_OSO: u8 = 0x66; /// local sym: name,,NO_SECT,type,offset pub const N_LSYM: u8 = 0x80; /// include file beginning: name,,NO_SECT,0,sum pub const N_BINCL: u8 = 0x82; /// #included file name: name,,n_sect,0,address pub const N_SOL: u8 = 0x84; /// compiler parameters: name,,NO_SECT,0,0 pub const N_PARAMS: u8 = 0x86; /// compiler version: name,,NO_SECT,0,0 pub const N_VERSION: u8 = 0x88; /// compiler -O level: name,,NO_SECT,0,0 pub const N_OLEVEL: u8 = 0x8A; /// parameter: name,,NO_SECT,type,offset pub const N_PSYM: u8 = 0xa0; /// include file end: name,,NO_SECT,0,0 pub const N_EINCL: u8 = 0xa2; /// alternate entry: name,,n_sect,linenumber,address pub const N_ENTRY: u8 = 0xa4; /// left bracket: 0,,NO_SECT,nesting level,address pub const N_LBRAC: u8 = 0xc0; /// deleted include file: name,,NO_SECT,0,sum pub const N_EXCL: u8 = 0xc2; /// right bracket: 0,,NO_SECT,nesting level,address pub const N_RBRAC: u8 = 0xe0; /// begin common: name,,NO_SECT,0,0 pub const N_BCOMM: u8 = 0xe2; /// end common: name,,n_sect,0,0 pub const N_ECOMM: u8 = 0xe4; /// end common (local name): 0,,n_sect,0,address pub const N_ECOML: u8 = 0xe8; /// second stab entry with length information pub const N_LENG: u8 = 0xfe; /* * for the berkeley pascal compiler, pc(1): */ /// global pascal symbol: name,,NO_SECT,subtype,line pub const N_PC: u8 = 0x30; // Definitions from "/usr/include/mach-o/reloc.h". /// A relocation entry. /// /// Mach-O relocations have plain and scattered variants, with the /// meaning of the fields depending on the variant. /// /// This type provides functions for determining whether the relocation /// is scattered, and for accessing the fields of each variant. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Relocation { pub r_word0: U32, pub r_word1: U32, } impl Relocation { /// Determine whether this is a scattered relocation. #[inline] pub fn r_scattered(self, endian: E, cputype: u32) -> bool { if cputype == CPU_TYPE_X86_64 { false } else { self.r_word0.get(endian) & R_SCATTERED != 0 } } /// Return the fields of a plain relocation. pub fn info(self, endian: E) -> RelocationInfo { let r_address = self.r_word0.get(endian); let r_word1 = self.r_word1.get(endian); if endian.is_little_endian() { RelocationInfo { r_address, r_symbolnum: r_word1 & 0x00ff_ffff, r_pcrel: ((r_word1 >> 24) & 0x1) != 0, r_length: ((r_word1 >> 25) & 0x3) as u8, r_extern: ((r_word1 >> 27) & 0x1) != 0, r_type: (r_word1 >> 28) as u8, } } else { RelocationInfo { r_address, r_symbolnum: r_word1 >> 8, r_pcrel: ((r_word1 >> 7) & 0x1) != 0, r_length: ((r_word1 >> 5) & 0x3) as u8, r_extern: ((r_word1 >> 4) & 0x1) != 0, r_type: (r_word1 & 0xf) as u8, } } } /// Return the fields of a scattered relocation. pub fn scattered_info(self, endian: E) -> ScatteredRelocationInfo { let r_word0 = self.r_word0.get(endian); let r_value = self.r_word1.get(endian); ScatteredRelocationInfo { r_address: r_word0 & 0x00ff_ffff, r_type: ((r_word0 >> 24) & 0xf) as u8, r_length: ((r_word0 >> 28) & 0x3) as u8, r_pcrel: ((r_word0 >> 30) & 0x1) != 0, r_value, } } } /* * Format of a relocation entry of a Mach-O file. Modified from the 4.3BSD * format. The modifications from the original format were changing the value * of the r_symbolnum field for "local" (r_extern == 0) relocation entries. * This modification is required to support symbols in an arbitrary number of * sections not just the three sections (text, data and bss) in a 4.3BSD file. * Also the last 4 bits have had the r_type tag added to them. */ #[derive(Debug, Clone, Copy)] pub struct RelocationInfo { /// offset in the section to what is being relocated pub r_address: u32, /// symbol index if r_extern == 1 or section ordinal if r_extern == 0 pub r_symbolnum: u32, /// was relocated pc relative already pub r_pcrel: bool, /// 0=byte, 1=word, 2=long, 3=quad pub r_length: u8, /// does not include value of sym referenced pub r_extern: bool, /// if not 0, machine specific relocation type pub r_type: u8, } impl RelocationInfo { /// Combine the fields into a `Relocation`. pub fn relocation(self, endian: E) -> Relocation { let r_word0 = U32::new(endian, self.r_address); let r_word1 = U32::new( endian, if endian.is_little_endian() { self.r_symbolnum & 0x00ff_ffff | u32::from(self.r_pcrel) << 24 | u32::from(self.r_length & 0x3) << 25 | u32::from(self.r_extern) << 27 | u32::from(self.r_type) << 28 } else { self.r_symbolnum >> 8 | u32::from(self.r_pcrel) << 7 | u32::from(self.r_length & 0x3) << 5 | u32::from(self.r_extern) << 4 | u32::from(self.r_type) & 0xf }, ); Relocation { r_word0, r_word1 } } } /// absolute relocation type for Mach-O files pub const R_ABS: u8 = 0; /* * The r_address is not really the address as it's name indicates but an offset. * In 4.3BSD a.out objects this offset is from the start of the "segment" for * which relocation entry is for (text or data). For Mach-O object files it is * also an offset but from the start of the "section" for which the relocation * entry is for. See comments in about the r_address feild * in images for used with the dynamic linker. * * In 4.3BSD a.out objects if r_extern is zero then r_symbolnum is an ordinal * for the segment the symbol being relocated is in. These ordinals are the * symbol types N_TEXT, N_DATA, N_BSS or N_ABS. In Mach-O object files these * ordinals refer to the sections in the object file in the order their section * structures appear in the headers of the object file they are in. The first * section has the ordinal 1, the second 2, and so on. This means that the * same ordinal in two different object files could refer to two different * sections. And further could have still different ordinals when combined * by the link-editor. The value R_ABS is used for relocation entries for * absolute symbols which need no further relocation. */ /* * For RISC machines some of the references are split across two instructions * and the instruction does not contain the complete value of the reference. * In these cases a second, or paired relocation entry, follows each of these * relocation entries, using a PAIR r_type, which contains the other part of the * reference not contained in the instruction. This other part is stored in the * pair's r_address field. The exact number of bits of the other part of the * reference store in the r_address field is dependent on the particular * relocation type for the particular architecture. */ /* * To make scattered loading by the link editor work correctly "local" * relocation entries can't be used when the item to be relocated is the value * of a symbol plus an offset (where the resulting expression is outside the * block the link editor is moving, a blocks are divided at symbol addresses). * In this case. where the item is a symbol value plus offset, the link editor * needs to know more than just the section the symbol was defined. What is * needed is the actual value of the symbol without the offset so it can do the * relocation correctly based on where the value of the symbol got relocated to * not the value of the expression (with the offset added to the symbol value). * So for the NeXT 2.0 release no "local" relocation entries are ever used when * there is a non-zero offset added to a symbol. The "external" and "local" * relocation entries remain unchanged. * * The implementation is quite messy given the compatibility with the existing * relocation entry format. The ASSUMPTION is that a section will never be * bigger than 2**24 - 1 (0x00ffffff or 16,777,215) bytes. This assumption * allows the r_address (which is really an offset) to fit in 24 bits and high * bit of the r_address field in the relocation_info structure to indicate * it is really a scattered_relocation_info structure. Since these are only * used in places where "local" relocation entries are used and not where * "external" relocation entries are used the r_extern field has been removed. * * For scattered loading to work on a RISC machine where some of the references * are split across two instructions the link editor needs to be assured that * each reference has a unique 32 bit reference (that more than one reference is * NOT sharing the same high 16 bits for example) so it move each referenced * item independent of each other. Some compilers guarantees this but the * compilers don't so scattered loading can be done on those that do guarantee * this. */ /// Bit set in `Relocation::r_word0` for scattered relocations. pub const R_SCATTERED: u32 = 0x8000_0000; #[derive(Debug, Clone, Copy)] pub struct ScatteredRelocationInfo { /// offset in the section to what is being relocated pub r_address: u32, /// if not 0, machine specific relocation type pub r_type: u8, /// 0=byte, 1=word, 2=long, 3=quad pub r_length: u8, /// was relocated pc relative already pub r_pcrel: bool, /// the value the item to be relocated is referring to (without any offset added) pub r_value: u32, } impl ScatteredRelocationInfo { /// Combine the fields into a `Relocation`. pub fn relocation(self, endian: E) -> Relocation { let r_word0 = U32::new( endian, self.r_address & 0x00ff_ffff | u32::from(self.r_type & 0xf) << 24 | u32::from(self.r_length & 0x3) << 28 | u32::from(self.r_pcrel) << 30 | R_SCATTERED, ); let r_word1 = U32::new(endian, self.r_value); Relocation { r_word0, r_word1 } } } /* * Relocation types used in a generic implementation. Relocation entries for * normal things use the generic relocation as described above and their r_type * is GENERIC_RELOC_VANILLA (a value of zero). * * Another type of generic relocation, GENERIC_RELOC_SECTDIFF, is to support * the difference of two symbols defined in different sections. That is the * expression "symbol1 - symbol2 + constant" is a relocatable expression when * both symbols are defined in some section. For this type of relocation the * both relocations entries are scattered relocation entries. The value of * symbol1 is stored in the first relocation entry's r_value field and the * value of symbol2 is stored in the pair's r_value field. * * A special case for a prebound lazy pointer is needed to beable to set the * value of the lazy pointer back to its non-prebound state. This is done * using the GENERIC_RELOC_PB_LA_PTR r_type. This is a scattered relocation * entry where the r_value feild is the value of the lazy pointer not prebound. */ /// generic relocation as described above pub const GENERIC_RELOC_VANILLA: u8 = 0; /// Only follows a GENERIC_RELOC_SECTDIFF pub const GENERIC_RELOC_PAIR: u8 = 1; pub const GENERIC_RELOC_SECTDIFF: u8 = 2; /// prebound lazy pointer pub const GENERIC_RELOC_PB_LA_PTR: u8 = 3; pub const GENERIC_RELOC_LOCAL_SECTDIFF: u8 = 4; /// thread local variables pub const GENERIC_RELOC_TLV: u8 = 5; // Definitions from "/usr/include/mach-o/arm/reloc.h". /* * Relocation types used in the arm implementation. Relocation entries for * things other than instructions use the same generic relocation as described * in and their r_type is ARM_RELOC_VANILLA, one of the * *_SECTDIFF or the *_PB_LA_PTR types. The rest of the relocation types are * for instructions. Since they are for instructions the r_address field * indicates the 32 bit instruction that the relocation is to be performed on. */ /// generic relocation as described above pub const ARM_RELOC_VANILLA: u8 = 0; /// the second relocation entry of a pair pub const ARM_RELOC_PAIR: u8 = 1; /// a PAIR follows with subtract symbol value pub const ARM_RELOC_SECTDIFF: u8 = 2; /// like ARM_RELOC_SECTDIFF, but the symbol referenced was local. pub const ARM_RELOC_LOCAL_SECTDIFF: u8 = 3; /// prebound lazy pointer pub const ARM_RELOC_PB_LA_PTR: u8 = 4; /// 24 bit branch displacement (to a word address) pub const ARM_RELOC_BR24: u8 = 5; /// 22 bit branch displacement (to a half-word address) pub const ARM_THUMB_RELOC_BR22: u8 = 6; /// obsolete - a thumb 32-bit branch instruction possibly needing page-spanning branch workaround pub const ARM_THUMB_32BIT_BRANCH: u8 = 7; /* * For these two r_type relocations they always have a pair following them * and the r_length bits are used differently. The encoding of the * r_length is as follows: * low bit of r_length: * 0 - :lower16: for movw instructions * 1 - :upper16: for movt instructions * high bit of r_length: * 0 - arm instructions * 1 - thumb instructions * the other half of the relocated expression is in the following pair * relocation entry in the the low 16 bits of r_address field. */ pub const ARM_RELOC_HALF: u8 = 8; pub const ARM_RELOC_HALF_SECTDIFF: u8 = 9; // Definitions from "/usr/include/mach-o/arm64/reloc.h". /* * Relocation types used in the arm64 implementation. */ /// for pointers pub const ARM64_RELOC_UNSIGNED: u8 = 0; /// must be followed by a ARM64_RELOC_UNSIGNED pub const ARM64_RELOC_SUBTRACTOR: u8 = 1; /// a B/BL instruction with 26-bit displacement pub const ARM64_RELOC_BRANCH26: u8 = 2; /// pc-rel distance to page of target pub const ARM64_RELOC_PAGE21: u8 = 3; /// offset within page, scaled by r_length pub const ARM64_RELOC_PAGEOFF12: u8 = 4; /// pc-rel distance to page of GOT slot pub const ARM64_RELOC_GOT_LOAD_PAGE21: u8 = 5; /// offset within page of GOT slot, scaled by r_length pub const ARM64_RELOC_GOT_LOAD_PAGEOFF12: u8 = 6; /// for pointers to GOT slots pub const ARM64_RELOC_POINTER_TO_GOT: u8 = 7; /// pc-rel distance to page of TLVP slot pub const ARM64_RELOC_TLVP_LOAD_PAGE21: u8 = 8; /// offset within page of TLVP slot, scaled by r_length pub const ARM64_RELOC_TLVP_LOAD_PAGEOFF12: u8 = 9; /// must be followed by PAGE21 or PAGEOFF12 pub const ARM64_RELOC_ADDEND: u8 = 10; // An arm64e authenticated pointer. // // Represents a pointer to a symbol (like ARM64_RELOC_UNSIGNED). // Additionally, the resulting pointer is signed. The signature is // specified in the target location: the addend is restricted to the lower // 32 bits (instead of the full 64 bits for ARM64_RELOC_UNSIGNED): // // |63|62|61-51|50-49| 48 |47 - 32|31 - 0| // | 1| 0| 0 | key | addr | discriminator | addend | // // The key is one of: // IA: 00 IB: 01 // DA: 10 DB: 11 // // The discriminator field is used as extra signature diversification. // // The addr field indicates whether the target address should be blended // into the discriminator. // pub const ARM64_RELOC_AUTHENTICATED_POINTER: u8 = 11; // Definitions from "/usr/include/mach-o/ppc/reloc.h". /* * Relocation types used in the ppc implementation. Relocation entries for * things other than instructions use the same generic relocation as described * above and their r_type is RELOC_VANILLA. The rest of the relocation types * are for instructions. Since they are for instructions the r_address field * indicates the 32 bit instruction that the relocation is to be performed on. * The fields r_pcrel and r_length are ignored for non-RELOC_VANILLA r_types * except for PPC_RELOC_BR14. * * For PPC_RELOC_BR14 if the r_length is the unused value 3, then the branch was * statically predicted setting or clearing the Y-bit based on the sign of the * displacement or the opcode. If this is the case the static linker must flip * the value of the Y-bit if the sign of the displacement changes for non-branch * always conditions. */ /// generic relocation as described above pub const PPC_RELOC_VANILLA: u8 = 0; /// the second relocation entry of a pair pub const PPC_RELOC_PAIR: u8 = 1; /// 14 bit branch displacement (to a word address) pub const PPC_RELOC_BR14: u8 = 2; /// 24 bit branch displacement (to a word address) pub const PPC_RELOC_BR24: u8 = 3; /// a PAIR follows with the low half pub const PPC_RELOC_HI16: u8 = 4; /// a PAIR follows with the high half pub const PPC_RELOC_LO16: u8 = 5; /// Same as the RELOC_HI16 except the low 16 bits and the high 16 bits are added together /// with the low 16 bits sign extended first. This means if bit 15 of the low 16 bits is /// set the high 16 bits stored in the instruction will be adjusted. pub const PPC_RELOC_HA16: u8 = 6; /// Same as the LO16 except that the low 2 bits are not stored in the instruction and are /// always zero. This is used in double word load/store instructions. pub const PPC_RELOC_LO14: u8 = 7; /// a PAIR follows with subtract symbol value pub const PPC_RELOC_SECTDIFF: u8 = 8; /// prebound lazy pointer pub const PPC_RELOC_PB_LA_PTR: u8 = 9; /// section difference forms of above. a PAIR pub const PPC_RELOC_HI16_SECTDIFF: u8 = 10; /// follows these with subtract symbol value pub const PPC_RELOC_LO16_SECTDIFF: u8 = 11; pub const PPC_RELOC_HA16_SECTDIFF: u8 = 12; pub const PPC_RELOC_JBSR: u8 = 13; pub const PPC_RELOC_LO14_SECTDIFF: u8 = 14; /// like PPC_RELOC_SECTDIFF, but the symbol referenced was local. pub const PPC_RELOC_LOCAL_SECTDIFF: u8 = 15; // Definitions from "/usr/include/mach-o/x86_64/reloc.h". /* * Relocations for x86_64 are a bit different than for other architectures in * Mach-O: Scattered relocations are not used. Almost all relocations produced * by the compiler are external relocations. An external relocation has the * r_extern bit set to 1 and the r_symbolnum field contains the symbol table * index of the target label. * * When the assembler is generating relocations, if the target label is a local * label (begins with 'L'), then the previous non-local label in the same * section is used as the target of the external relocation. An addend is used * with the distance from that non-local label to the target label. Only when * there is no previous non-local label in the section is an internal * relocation used. * * The addend (i.e. the 4 in _foo+4) is encoded in the instruction (Mach-O does * not have RELA relocations). For PC-relative relocations, the addend is * stored directly in the instruction. This is different from other Mach-O * architectures, which encode the addend minus the current section offset. * * The relocation types are: * * X86_64_RELOC_UNSIGNED // for absolute addresses * X86_64_RELOC_SIGNED // for signed 32-bit displacement * X86_64_RELOC_BRANCH // a CALL/JMP instruction with 32-bit displacement * X86_64_RELOC_GOT_LOAD // a MOVQ load of a GOT entry * X86_64_RELOC_GOT // other GOT references * X86_64_RELOC_SUBTRACTOR // must be followed by a X86_64_RELOC_UNSIGNED * * The following are sample assembly instructions, followed by the relocation * and section content they generate in an object file: * * call _foo * r_type=X86_64_RELOC_BRANCH, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo * E8 00 00 00 00 * * call _foo+4 * r_type=X86_64_RELOC_BRANCH, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo * E8 04 00 00 00 * * movq _foo@GOTPCREL(%rip), %rax * r_type=X86_64_RELOC_GOT_LOAD, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo * 48 8B 05 00 00 00 00 * * pushq _foo@GOTPCREL(%rip) * r_type=X86_64_RELOC_GOT, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo * FF 35 00 00 00 00 * * movl _foo(%rip), %eax * r_type=X86_64_RELOC_SIGNED, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo * 8B 05 00 00 00 00 * * movl _foo+4(%rip), %eax * r_type=X86_64_RELOC_SIGNED, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo * 8B 05 04 00 00 00 * * movb $0x12, _foo(%rip) * r_type=X86_64_RELOC_SIGNED, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo * C6 05 FF FF FF FF 12 * * movl $0x12345678, _foo(%rip) * r_type=X86_64_RELOC_SIGNED, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_foo * C7 05 FC FF FF FF 78 56 34 12 * * .quad _foo * r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_foo * 00 00 00 00 00 00 00 00 * * .quad _foo+4 * r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_foo * 04 00 00 00 00 00 00 00 * * .quad _foo - _bar * r_type=X86_64_RELOC_SUBTRACTOR, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_bar * r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_foo * 00 00 00 00 00 00 00 00 * * .quad _foo - _bar + 4 * r_type=X86_64_RELOC_SUBTRACTOR, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_bar * r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_foo * 04 00 00 00 00 00 00 00 * * .long _foo - _bar * r_type=X86_64_RELOC_SUBTRACTOR, r_length=2, r_extern=1, r_pcrel=0, r_symbolnum=_bar * r_type=X86_64_RELOC_UNSIGNED, r_length=2, r_extern=1, r_pcrel=0, r_symbolnum=_foo * 00 00 00 00 * * lea L1(%rip), %rax * r_type=X86_64_RELOC_SIGNED, r_length=2, r_extern=1, r_pcrel=1, r_symbolnum=_prev * 48 8d 05 12 00 00 00 * // assumes _prev is the first non-local label 0x12 bytes before L1 * * lea L0(%rip), %rax * r_type=X86_64_RELOC_SIGNED, r_length=2, r_extern=0, r_pcrel=1, r_symbolnum=3 * 48 8d 05 56 00 00 00 * // assumes L0 is in third section and there is no previous non-local label. * // The rip-relative-offset of 0x00000056 is L0-address_of_next_instruction. * // address_of_next_instruction is the address of the relocation + 4. * * add $6,L0(%rip) * r_type=X86_64_RELOC_SIGNED_1, r_length=2, r_extern=0, r_pcrel=1, r_symbolnum=3 * 83 05 18 00 00 00 06 * // assumes L0 is in third section and there is no previous non-local label. * // The rip-relative-offset of 0x00000018 is L0-address_of_next_instruction. * // address_of_next_instruction is the address of the relocation + 4 + 1. * // The +1 comes from SIGNED_1. This is used because the relocation is not * // at the end of the instruction. * * .quad L1 * r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_prev * 12 00 00 00 00 00 00 00 * // assumes _prev is the first non-local label 0x12 bytes before L1 * * .quad L0 * r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=0, r_pcrel=0, r_symbolnum=3 * 56 00 00 00 00 00 00 00 * // assumes L0 is in third section, has an address of 0x00000056 in .o * // file, and there is no previous non-local label * * .quad _foo - . * r_type=X86_64_RELOC_SUBTRACTOR, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_prev * r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_foo * EE FF FF FF FF FF FF FF * // assumes _prev is the first non-local label 0x12 bytes before this * // .quad * * .quad _foo - L1 * r_type=X86_64_RELOC_SUBTRACTOR, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_prev * r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_extern=1, r_pcrel=0, r_symbolnum=_foo * EE FF FF FF FF FF FF FF * // assumes _prev is the first non-local label 0x12 bytes before L1 * * .quad L1 - _prev * // No relocations. This is an assembly time constant. * 12 00 00 00 00 00 00 00 * // assumes _prev is the first non-local label 0x12 bytes before L1 * * * * In final linked images, there are only two valid relocation kinds: * * r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_pcrel=0, r_extern=1, r_symbolnum=sym_index * This tells dyld to add the address of a symbol to a pointer sized (8-byte) * piece of data (i.e on disk the 8-byte piece of data contains the addend). The * r_symbolnum contains the index into the symbol table of the target symbol. * * r_type=X86_64_RELOC_UNSIGNED, r_length=3, r_pcrel=0, r_extern=0, r_symbolnum=0 * This tells dyld to adjust the pointer sized (8-byte) piece of data by the amount * the containing image was loaded from its base address (e.g. slide). * */ /// for absolute addresses pub const X86_64_RELOC_UNSIGNED: u8 = 0; /// for signed 32-bit displacement pub const X86_64_RELOC_SIGNED: u8 = 1; /// a CALL/JMP instruction with 32-bit displacement pub const X86_64_RELOC_BRANCH: u8 = 2; /// a MOVQ load of a GOT entry pub const X86_64_RELOC_GOT_LOAD: u8 = 3; /// other GOT references pub const X86_64_RELOC_GOT: u8 = 4; /// must be followed by a X86_64_RELOC_UNSIGNED pub const X86_64_RELOC_SUBTRACTOR: u8 = 5; /// for signed 32-bit displacement with a -1 addend pub const X86_64_RELOC_SIGNED_1: u8 = 6; /// for signed 32-bit displacement with a -2 addend pub const X86_64_RELOC_SIGNED_2: u8 = 7; /// for signed 32-bit displacement with a -4 addend pub const X86_64_RELOC_SIGNED_4: u8 = 8; /// for thread local variables pub const X86_64_RELOC_TLV: u8 = 9; unsafe_impl_pod!(FatHeader, FatArch32, FatArch64,); unsafe_impl_endian_pod!( DyldCacheHeader, DyldCacheMappingInfo, DyldCacheImageInfo, DyldSubCacheEntryV1, DyldSubCacheEntryV2, MachHeader32, MachHeader64, LoadCommand, LcStr, SegmentCommand32, SegmentCommand64, Section32, Section64, Fvmlib, FvmlibCommand, Dylib, DylibCommand, SubFrameworkCommand, SubClientCommand, SubUmbrellaCommand, SubLibraryCommand, PreboundDylibCommand, DylinkerCommand, ThreadCommand, RoutinesCommand32, RoutinesCommand64, SymtabCommand, DysymtabCommand, DylibTableOfContents, DylibModule32, DylibModule64, DylibReference, TwolevelHintsCommand, TwolevelHint, PrebindCksumCommand, UuidCommand, RpathCommand, LinkeditDataCommand, FilesetEntryCommand, EncryptionInfoCommand32, EncryptionInfoCommand64, VersionMinCommand, BuildVersionCommand, BuildToolVersion, DyldInfoCommand, LinkerOptionCommand, SymsegCommand, IdentCommand, FvmfileCommand, EntryPointCommand, SourceVersionCommand, DataInCodeEntry, //TlvDescriptor, NoteCommand, Nlist32, Nlist64, Relocation, ); object-0.36.5/src/pe.rs000064400000000000000000003064211046102023000127340ustar 00000000000000//! PE/COFF definitions. //! //! These definitions are independent of read/write support, although we do implement //! some traits useful for those. //! //! This module is based heavily on "winnt.h" (10.0.17763.0). #![allow(missing_docs)] use core::convert::TryInto; use crate::endian::{I32Bytes, LittleEndian as LE, U16Bytes, U32Bytes, I32, U16, U32, U64}; use crate::pod::Pod; /// MZ pub const IMAGE_DOS_SIGNATURE: u16 = 0x5A4D; /// NE pub const IMAGE_OS2_SIGNATURE: u16 = 0x454E; /// LE pub const IMAGE_OS2_SIGNATURE_LE: u16 = 0x454C; /// LE pub const IMAGE_VXD_SIGNATURE: u16 = 0x454C; /// PE00 pub const IMAGE_NT_SIGNATURE: u32 = 0x0000_4550; /// DOS .EXE header #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageDosHeader { /// Magic number pub e_magic: U16, /// Bytes on last page of file pub e_cblp: U16, /// Pages in file pub e_cp: U16, /// Relocations pub e_crlc: U16, /// Size of header in paragraphs pub e_cparhdr: U16, /// Minimum extra paragraphs needed pub e_minalloc: U16, /// Maximum extra paragraphs needed pub e_maxalloc: U16, /// Initial (relative) SS value pub e_ss: U16, /// Initial SP value pub e_sp: U16, /// Checksum pub e_csum: U16, /// Initial IP value pub e_ip: U16, /// Initial (relative) CS value pub e_cs: U16, /// File address of relocation table pub e_lfarlc: U16, /// Overlay number pub e_ovno: U16, /// Reserved words pub e_res: [U16; 4], /// OEM identifier (for e_oeminfo) pub e_oemid: U16, /// OEM information; e_oemid specific pub e_oeminfo: U16, /// Reserved words pub e_res2: [U16; 10], /// File address of new exe header pub e_lfanew: U32, } /// OS/2 .EXE header #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageOs2Header { /// Magic number pub ne_magic: U16, /// Version number pub ne_ver: i8, /// Revision number pub ne_rev: i8, /// Offset of Entry Table pub ne_enttab: U16, /// Number of bytes in Entry Table pub ne_cbenttab: U16, /// Checksum of whole file pub ne_crc: I32, /// Flag word pub ne_flags: U16, /// Automatic data segment number pub ne_autodata: U16, /// Initial heap allocation pub ne_heap: U16, /// Initial stack allocation pub ne_stack: U16, /// Initial CS:IP setting pub ne_csip: I32, /// Initial SS:SP setting pub ne_sssp: I32, /// Count of file segments pub ne_cseg: U16, /// Entries in Module Reference Table pub ne_cmod: U16, /// Size of non-resident name table pub ne_cbnrestab: U16, /// Offset of Segment Table pub ne_segtab: U16, /// Offset of Resource Table pub ne_rsrctab: U16, /// Offset of resident name table pub ne_restab: U16, /// Offset of Module Reference Table pub ne_modtab: U16, /// Offset of Imported Names Table pub ne_imptab: U16, /// Offset of Non-resident Names Table pub ne_nrestab: I32, /// Count of movable entries pub ne_cmovent: U16, /// Segment alignment shift count pub ne_align: U16, /// Count of resource segments pub ne_cres: U16, /// Target Operating system pub ne_exetyp: u8, /// Other .EXE flags pub ne_flagsothers: u8, /// offset to return thunks pub ne_pretthunks: U16, /// offset to segment ref. bytes pub ne_psegrefbytes: U16, /// Minimum code swap area size pub ne_swaparea: U16, /// Expected Windows version number pub ne_expver: U16, } /// Windows VXD header #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageVxdHeader { /// Magic number pub e32_magic: U16, /// The byte ordering for the VXD pub e32_border: u8, /// The word ordering for the VXD pub e32_worder: u8, /// The EXE format level for now = 0 pub e32_level: U32, /// The CPU type pub e32_cpu: U16, /// The OS type pub e32_os: U16, /// Module version pub e32_ver: U32, /// Module flags pub e32_mflags: U32, /// Module # pages pub e32_mpages: U32, /// Object # for instruction pointer pub e32_startobj: U32, /// Extended instruction pointer pub e32_eip: U32, /// Object # for stack pointer pub e32_stackobj: U32, /// Extended stack pointer pub e32_esp: U32, /// VXD page size pub e32_pagesize: U32, /// Last page size in VXD pub e32_lastpagesize: U32, /// Fixup section size pub e32_fixupsize: U32, /// Fixup section checksum pub e32_fixupsum: U32, /// Loader section size pub e32_ldrsize: U32, /// Loader section checksum pub e32_ldrsum: U32, /// Object table offset pub e32_objtab: U32, /// Number of objects in module pub e32_objcnt: U32, /// Object page map offset pub e32_objmap: U32, /// Object iterated data map offset pub e32_itermap: U32, /// Offset of Resource Table pub e32_rsrctab: U32, /// Number of resource entries pub e32_rsrccnt: U32, /// Offset of resident name table pub e32_restab: U32, /// Offset of Entry Table pub e32_enttab: U32, /// Offset of Module Directive Table pub e32_dirtab: U32, /// Number of module directives pub e32_dircnt: U32, /// Offset of Fixup Page Table pub e32_fpagetab: U32, /// Offset of Fixup Record Table pub e32_frectab: U32, /// Offset of Import Module Name Table pub e32_impmod: U32, /// Number of entries in Import Module Name Table pub e32_impmodcnt: U32, /// Offset of Import Procedure Name Table pub e32_impproc: U32, /// Offset of Per-Page Checksum Table pub e32_pagesum: U32, /// Offset of Enumerated Data Pages pub e32_datapage: U32, /// Number of preload pages pub e32_preload: U32, /// Offset of Non-resident Names Table pub e32_nrestab: U32, /// Size of Non-resident Name Table pub e32_cbnrestab: U32, /// Non-resident Name Table Checksum pub e32_nressum: U32, /// Object # for automatic data object pub e32_autodata: U32, /// Offset of the debugging information pub e32_debuginfo: U32, /// The length of the debugging info. in bytes pub e32_debuglen: U32, /// Number of instance pages in preload section of VXD file pub e32_instpreload: U32, /// Number of instance pages in demand load section of VXD file pub e32_instdemand: U32, /// Size of heap - for 16-bit apps pub e32_heapsize: U32, /// Reserved words pub e32_res3: [u8; 12], pub e32_winresoff: U32, pub e32_winreslen: U32, /// Device ID for VxD pub e32_devid: U16, /// DDK version for VxD pub e32_ddkver: U16, } /// A PE rich header entry. /// /// Rich headers have no official documentation, but have been heavily /// reversed-engineered and documented in the wild, e.g.: /// * `http://www.ntcore.com/files/richsign.htm` /// * `https://www.researchgate.net/figure/Structure-of-the-Rich-Header_fig1_318145388` /// /// This data is "masked", i.e. XORed with a checksum derived from the file data. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct MaskedRichHeaderEntry { pub masked_comp_id: U32, pub masked_count: U32, } // // File header format. // #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageFileHeader { pub machine: U16, pub number_of_sections: U16, pub time_date_stamp: U32, pub pointer_to_symbol_table: U32, pub number_of_symbols: U32, pub size_of_optional_header: U16, pub characteristics: U16, } pub const IMAGE_SIZEOF_FILE_HEADER: usize = 20; /// Relocation info stripped from file. pub const IMAGE_FILE_RELOCS_STRIPPED: u16 = 0x0001; /// File is executable (i.e. no unresolved external references). pub const IMAGE_FILE_EXECUTABLE_IMAGE: u16 = 0x0002; /// Line numbers stripped from file. pub const IMAGE_FILE_LINE_NUMS_STRIPPED: u16 = 0x0004; /// Local symbols stripped from file. pub const IMAGE_FILE_LOCAL_SYMS_STRIPPED: u16 = 0x0008; /// Aggressively trim working set pub const IMAGE_FILE_AGGRESIVE_WS_TRIM: u16 = 0x0010; /// App can handle >2gb addresses pub const IMAGE_FILE_LARGE_ADDRESS_AWARE: u16 = 0x0020; /// Bytes of machine word are reversed. pub const IMAGE_FILE_BYTES_REVERSED_LO: u16 = 0x0080; /// 32 bit word machine. pub const IMAGE_FILE_32BIT_MACHINE: u16 = 0x0100; /// Debugging info stripped from file in .DBG file pub const IMAGE_FILE_DEBUG_STRIPPED: u16 = 0x0200; /// If Image is on removable media, copy and run from the swap file. pub const IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP: u16 = 0x0400; /// If Image is on Net, copy and run from the swap file. pub const IMAGE_FILE_NET_RUN_FROM_SWAP: u16 = 0x0800; /// System File. pub const IMAGE_FILE_SYSTEM: u16 = 0x1000; /// File is a DLL. pub const IMAGE_FILE_DLL: u16 = 0x2000; /// File should only be run on a UP machine pub const IMAGE_FILE_UP_SYSTEM_ONLY: u16 = 0x4000; /// Bytes of machine word are reversed. pub const IMAGE_FILE_BYTES_REVERSED_HI: u16 = 0x8000; pub const IMAGE_FILE_MACHINE_UNKNOWN: u16 = 0; /// Useful for indicating we want to interact with the host and not a WoW guest. pub const IMAGE_FILE_MACHINE_TARGET_HOST: u16 = 0x0001; /// Intel 386. pub const IMAGE_FILE_MACHINE_I386: u16 = 0x014c; /// MIPS little-endian, 0x160 big-endian pub const IMAGE_FILE_MACHINE_R3000: u16 = 0x0162; /// MIPS little-endian pub const IMAGE_FILE_MACHINE_R4000: u16 = 0x0166; /// MIPS little-endian pub const IMAGE_FILE_MACHINE_R10000: u16 = 0x0168; /// MIPS little-endian WCE v2 pub const IMAGE_FILE_MACHINE_WCEMIPSV2: u16 = 0x0169; /// Alpha_AXP pub const IMAGE_FILE_MACHINE_ALPHA: u16 = 0x0184; /// SH3 little-endian pub const IMAGE_FILE_MACHINE_SH3: u16 = 0x01a2; pub const IMAGE_FILE_MACHINE_SH3DSP: u16 = 0x01a3; /// SH3E little-endian pub const IMAGE_FILE_MACHINE_SH3E: u16 = 0x01a4; /// SH4 little-endian pub const IMAGE_FILE_MACHINE_SH4: u16 = 0x01a6; /// SH5 pub const IMAGE_FILE_MACHINE_SH5: u16 = 0x01a8; /// ARM Little-Endian pub const IMAGE_FILE_MACHINE_ARM: u16 = 0x01c0; /// ARM Thumb/Thumb-2 Little-Endian pub const IMAGE_FILE_MACHINE_THUMB: u16 = 0x01c2; /// ARM Thumb-2 Little-Endian pub const IMAGE_FILE_MACHINE_ARMNT: u16 = 0x01c4; pub const IMAGE_FILE_MACHINE_AM33: u16 = 0x01d3; /// IBM PowerPC Little-Endian pub const IMAGE_FILE_MACHINE_POWERPC: u16 = 0x01F0; pub const IMAGE_FILE_MACHINE_POWERPCFP: u16 = 0x01f1; /// Intel 64 pub const IMAGE_FILE_MACHINE_IA64: u16 = 0x0200; /// MIPS pub const IMAGE_FILE_MACHINE_MIPS16: u16 = 0x0266; /// ALPHA64 pub const IMAGE_FILE_MACHINE_ALPHA64: u16 = 0x0284; /// MIPS pub const IMAGE_FILE_MACHINE_MIPSFPU: u16 = 0x0366; /// MIPS pub const IMAGE_FILE_MACHINE_MIPSFPU16: u16 = 0x0466; pub const IMAGE_FILE_MACHINE_AXP64: u16 = IMAGE_FILE_MACHINE_ALPHA64; /// Infineon pub const IMAGE_FILE_MACHINE_TRICORE: u16 = 0x0520; pub const IMAGE_FILE_MACHINE_CEF: u16 = 0x0CEF; /// EFI Byte Code pub const IMAGE_FILE_MACHINE_EBC: u16 = 0x0EBC; /// AMD64 (K8) pub const IMAGE_FILE_MACHINE_AMD64: u16 = 0x8664; /// M32R little-endian pub const IMAGE_FILE_MACHINE_M32R: u16 = 0x9041; /// ARM64 Little-Endian pub const IMAGE_FILE_MACHINE_ARM64: u16 = 0xAA64; /// ARM64EC ("Emulation Compatible") pub const IMAGE_FILE_MACHINE_ARM64EC: u16 = 0xA641; pub const IMAGE_FILE_MACHINE_CEE: u16 = 0xC0EE; /// RISCV32 pub const IMAGE_FILE_MACHINE_RISCV32: u16 = 0x5032; /// RISCV64 pub const IMAGE_FILE_MACHINE_RISCV64: u16 = 0x5064; /// RISCV128 pub const IMAGE_FILE_MACHINE_RISCV128: u16 = 0x5128; /// ARM64X (Mixed ARM64 and ARM64EC) pub const IMAGE_FILE_MACHINE_ARM64X: u16 = 0xA64E; /// CHPE x86 ("Compiled Hybrid Portable Executable") pub const IMAGE_FILE_MACHINE_CHPE_X86: u16 = 0x3A64; // // Directory format. // #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageDataDirectory { pub virtual_address: U32, pub size: U32, } pub const IMAGE_NUMBEROF_DIRECTORY_ENTRIES: usize = 16; // // Optional header format. // #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageOptionalHeader32 { // Standard fields. pub magic: U16, pub major_linker_version: u8, pub minor_linker_version: u8, pub size_of_code: U32, pub size_of_initialized_data: U32, pub size_of_uninitialized_data: U32, pub address_of_entry_point: U32, pub base_of_code: U32, pub base_of_data: U32, // NT additional fields. pub image_base: U32, pub section_alignment: U32, pub file_alignment: U32, pub major_operating_system_version: U16, pub minor_operating_system_version: U16, pub major_image_version: U16, pub minor_image_version: U16, pub major_subsystem_version: U16, pub minor_subsystem_version: U16, pub win32_version_value: U32, pub size_of_image: U32, pub size_of_headers: U32, pub check_sum: U32, pub subsystem: U16, pub dll_characteristics: U16, pub size_of_stack_reserve: U32, pub size_of_stack_commit: U32, pub size_of_heap_reserve: U32, pub size_of_heap_commit: U32, pub loader_flags: U32, pub number_of_rva_and_sizes: U32, //pub data_directory: [ImageDataDirectory; IMAGE_NUMBEROF_DIRECTORY_ENTRIES], } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageRomOptionalHeader { pub magic: U16, pub major_linker_version: u8, pub minor_linker_version: u8, pub size_of_code: U32, pub size_of_initialized_data: U32, pub size_of_uninitialized_data: U32, pub address_of_entry_point: U32, pub base_of_code: U32, pub base_of_data: U32, pub base_of_bss: U32, pub gpr_mask: U32, pub cpr_mask: [U32; 4], pub gp_value: U32, } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageOptionalHeader64 { pub magic: U16, pub major_linker_version: u8, pub minor_linker_version: u8, pub size_of_code: U32, pub size_of_initialized_data: U32, pub size_of_uninitialized_data: U32, pub address_of_entry_point: U32, pub base_of_code: U32, pub image_base: U64, pub section_alignment: U32, pub file_alignment: U32, pub major_operating_system_version: U16, pub minor_operating_system_version: U16, pub major_image_version: U16, pub minor_image_version: U16, pub major_subsystem_version: U16, pub minor_subsystem_version: U16, pub win32_version_value: U32, pub size_of_image: U32, pub size_of_headers: U32, pub check_sum: U32, pub subsystem: U16, pub dll_characteristics: U16, pub size_of_stack_reserve: U64, pub size_of_stack_commit: U64, pub size_of_heap_reserve: U64, pub size_of_heap_commit: U64, pub loader_flags: U32, pub number_of_rva_and_sizes: U32, //pub data_directory: [ImageDataDirectory; IMAGE_NUMBEROF_DIRECTORY_ENTRIES], } pub const IMAGE_NT_OPTIONAL_HDR32_MAGIC: u16 = 0x10b; pub const IMAGE_NT_OPTIONAL_HDR64_MAGIC: u16 = 0x20b; pub const IMAGE_ROM_OPTIONAL_HDR_MAGIC: u16 = 0x107; #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageNtHeaders64 { pub signature: U32, pub file_header: ImageFileHeader, pub optional_header: ImageOptionalHeader64, } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageNtHeaders32 { pub signature: U32, pub file_header: ImageFileHeader, pub optional_header: ImageOptionalHeader32, } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageRomHeaders { pub file_header: ImageFileHeader, pub optional_header: ImageRomOptionalHeader, } // Values for `ImageOptionalHeader*::subsystem`. /// Unknown subsystem. pub const IMAGE_SUBSYSTEM_UNKNOWN: u16 = 0; /// Image doesn't require a subsystem. pub const IMAGE_SUBSYSTEM_NATIVE: u16 = 1; /// Image runs in the Windows GUI subsystem. pub const IMAGE_SUBSYSTEM_WINDOWS_GUI: u16 = 2; /// Image runs in the Windows character subsystem. pub const IMAGE_SUBSYSTEM_WINDOWS_CUI: u16 = 3; /// image runs in the OS/2 character subsystem. pub const IMAGE_SUBSYSTEM_OS2_CUI: u16 = 5; /// image runs in the Posix character subsystem. pub const IMAGE_SUBSYSTEM_POSIX_CUI: u16 = 7; /// image is a native Win9x driver. pub const IMAGE_SUBSYSTEM_NATIVE_WINDOWS: u16 = 8; /// Image runs in the Windows CE subsystem. pub const IMAGE_SUBSYSTEM_WINDOWS_CE_GUI: u16 = 9; pub const IMAGE_SUBSYSTEM_EFI_APPLICATION: u16 = 10; pub const IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER: u16 = 11; pub const IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER: u16 = 12; pub const IMAGE_SUBSYSTEM_EFI_ROM: u16 = 13; pub const IMAGE_SUBSYSTEM_XBOX: u16 = 14; pub const IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION: u16 = 16; pub const IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG: u16 = 17; // Values for `ImageOptionalHeader*::dll_characteristics`. // IMAGE_LIBRARY_PROCESS_INIT 0x0001 // Reserved. // IMAGE_LIBRARY_PROCESS_TERM 0x0002 // Reserved. // IMAGE_LIBRARY_THREAD_INIT 0x0004 // Reserved. // IMAGE_LIBRARY_THREAD_TERM 0x0008 // Reserved. /// Image can handle a high entropy 64-bit virtual address space. pub const IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA: u16 = 0x0020; /// DLL can move. pub const IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE: u16 = 0x0040; /// Code Integrity Image pub const IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY: u16 = 0x0080; /// Image is NX compatible pub const IMAGE_DLLCHARACTERISTICS_NX_COMPAT: u16 = 0x0100; /// Image understands isolation and doesn't want it pub const IMAGE_DLLCHARACTERISTICS_NO_ISOLATION: u16 = 0x0200; /// Image does not use SEH. No SE handler may reside in this image pub const IMAGE_DLLCHARACTERISTICS_NO_SEH: u16 = 0x0400; /// Do not bind this image. pub const IMAGE_DLLCHARACTERISTICS_NO_BIND: u16 = 0x0800; /// Image should execute in an AppContainer pub const IMAGE_DLLCHARACTERISTICS_APPCONTAINER: u16 = 0x1000; /// Driver uses WDM model pub const IMAGE_DLLCHARACTERISTICS_WDM_DRIVER: u16 = 0x2000; /// Image supports Control Flow Guard. pub const IMAGE_DLLCHARACTERISTICS_GUARD_CF: u16 = 0x4000; pub const IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE: u16 = 0x8000; // Indices for `ImageOptionalHeader*::data_directory`. /// Export Directory pub const IMAGE_DIRECTORY_ENTRY_EXPORT: usize = 0; /// Import Directory pub const IMAGE_DIRECTORY_ENTRY_IMPORT: usize = 1; /// Resource Directory pub const IMAGE_DIRECTORY_ENTRY_RESOURCE: usize = 2; /// Exception Directory pub const IMAGE_DIRECTORY_ENTRY_EXCEPTION: usize = 3; /// Security Directory pub const IMAGE_DIRECTORY_ENTRY_SECURITY: usize = 4; /// Base Relocation Table pub const IMAGE_DIRECTORY_ENTRY_BASERELOC: usize = 5; /// Debug Directory pub const IMAGE_DIRECTORY_ENTRY_DEBUG: usize = 6; // IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage) /// Architecture Specific Data pub const IMAGE_DIRECTORY_ENTRY_ARCHITECTURE: usize = 7; /// RVA of GP pub const IMAGE_DIRECTORY_ENTRY_GLOBALPTR: usize = 8; /// TLS Directory pub const IMAGE_DIRECTORY_ENTRY_TLS: usize = 9; /// Load Configuration Directory pub const IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG: usize = 10; /// Bound Import Directory in headers pub const IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT: usize = 11; /// Import Address Table pub const IMAGE_DIRECTORY_ENTRY_IAT: usize = 12; /// Delay Load Import Descriptors pub const IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT: usize = 13; /// COM Runtime descriptor pub const IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR: usize = 14; #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(C)] pub struct Guid(pub [u8; 16]); impl Guid { #[inline] pub fn data1(self) -> U32 { U32::from_bytes(self.0[0..4].try_into().unwrap()) } #[inline] pub fn data2(self) -> U16 { U16::from_bytes(self.0[4..6].try_into().unwrap()) } #[inline] pub fn data3(self) -> U16 { U16::from_bytes(self.0[6..8].try_into().unwrap()) } #[inline] pub fn data4(self) -> [u8; 8] { self.0[8..16].try_into().unwrap() } } pub use Guid as ClsId; /// Non-COFF Object file header #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct AnonObjectHeader { /// Must be IMAGE_FILE_MACHINE_UNKNOWN pub sig1: U16, /// Must be 0xffff pub sig2: U16, /// >= 1 (implies the ClsId field is present) pub version: U16, pub machine: U16, pub time_date_stamp: U32, /// Used to invoke CoCreateInstance pub class_id: ClsId, /// Size of data that follows the header pub size_of_data: U32, } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct AnonObjectHeaderV2 { /// Must be IMAGE_FILE_MACHINE_UNKNOWN pub sig1: U16, /// Must be 0xffff pub sig2: U16, /// >= 2 (implies the Flags field is present - otherwise V1) pub version: U16, pub machine: U16, pub time_date_stamp: U32, /// Used to invoke CoCreateInstance pub class_id: ClsId, /// Size of data that follows the header pub size_of_data: U32, /// 0x1 -> contains metadata pub flags: U32, /// Size of CLR metadata pub meta_data_size: U32, /// Offset of CLR metadata pub meta_data_offset: U32, } /// The required value of `AnonObjectHeaderBigobj::class_id`. pub const ANON_OBJECT_HEADER_BIGOBJ_CLASS_ID: ClsId = ClsId([ 0xC7, 0xA1, 0xBA, 0xD1, 0xEE, 0xBA, 0xA9, 0x4B, 0xAF, 0x20, 0xFA, 0xF6, 0x6A, 0xA4, 0xDC, 0xB8, ]); #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct AnonObjectHeaderBigobj { /* same as ANON_OBJECT_HEADER_V2 */ /// Must be IMAGE_FILE_MACHINE_UNKNOWN pub sig1: U16, /// Must be 0xffff pub sig2: U16, /// >= 2 (implies the Flags field is present) pub version: U16, /// Actual machine - IMAGE_FILE_MACHINE_xxx pub machine: U16, pub time_date_stamp: U32, /// Must be `ANON_OBJECT_HEADER_BIGOBJ_CLASS_ID`. pub class_id: ClsId, /// Size of data that follows the header pub size_of_data: U32, /// 0x1 -> contains metadata pub flags: U32, /// Size of CLR metadata pub meta_data_size: U32, /// Offset of CLR metadata pub meta_data_offset: U32, /* bigobj specifics */ /// extended from WORD pub number_of_sections: U32, pub pointer_to_symbol_table: U32, pub number_of_symbols: U32, } pub const IMAGE_SIZEOF_SHORT_NAME: usize = 8; // // Section header format. // #[derive(Debug, Default, Clone, Copy)] #[repr(C)] pub struct ImageSectionHeader { pub name: [u8; IMAGE_SIZEOF_SHORT_NAME], pub virtual_size: U32, pub virtual_address: U32, pub size_of_raw_data: U32, pub pointer_to_raw_data: U32, pub pointer_to_relocations: U32, pub pointer_to_linenumbers: U32, pub number_of_relocations: U16, pub number_of_linenumbers: U16, pub characteristics: U32, } pub const IMAGE_SIZEOF_SECTION_HEADER: usize = 40; // Values for `ImageSectionHeader::characteristics`. // IMAGE_SCN_TYPE_REG 0x00000000 // Reserved. // IMAGE_SCN_TYPE_DSECT 0x00000001 // Reserved. // IMAGE_SCN_TYPE_NOLOAD 0x00000002 // Reserved. // IMAGE_SCN_TYPE_GROUP 0x00000004 // Reserved. /// Reserved. pub const IMAGE_SCN_TYPE_NO_PAD: u32 = 0x0000_0008; // IMAGE_SCN_TYPE_COPY 0x00000010 // Reserved. /// Section contains code. pub const IMAGE_SCN_CNT_CODE: u32 = 0x0000_0020; /// Section contains initialized data. pub const IMAGE_SCN_CNT_INITIALIZED_DATA: u32 = 0x0000_0040; /// Section contains uninitialized data. pub const IMAGE_SCN_CNT_UNINITIALIZED_DATA: u32 = 0x0000_0080; /// Reserved. pub const IMAGE_SCN_LNK_OTHER: u32 = 0x0000_0100; /// Section contains comments or some other type of information. pub const IMAGE_SCN_LNK_INFO: u32 = 0x0000_0200; // IMAGE_SCN_TYPE_OVER 0x00000400 // Reserved. /// Section contents will not become part of image. pub const IMAGE_SCN_LNK_REMOVE: u32 = 0x0000_0800; /// Section contents comdat. pub const IMAGE_SCN_LNK_COMDAT: u32 = 0x0000_1000; // 0x00002000 // Reserved. // IMAGE_SCN_MEM_PROTECTED - Obsolete 0x00004000 /// Reset speculative exceptions handling bits in the TLB entries for this section. pub const IMAGE_SCN_NO_DEFER_SPEC_EXC: u32 = 0x0000_4000; /// Section content can be accessed relative to GP pub const IMAGE_SCN_GPREL: u32 = 0x0000_8000; pub const IMAGE_SCN_MEM_FARDATA: u32 = 0x0000_8000; // IMAGE_SCN_MEM_SYSHEAP - Obsolete 0x00010000 pub const IMAGE_SCN_MEM_PURGEABLE: u32 = 0x0002_0000; pub const IMAGE_SCN_MEM_16BIT: u32 = 0x0002_0000; pub const IMAGE_SCN_MEM_LOCKED: u32 = 0x0004_0000; pub const IMAGE_SCN_MEM_PRELOAD: u32 = 0x0008_0000; pub const IMAGE_SCN_ALIGN_1BYTES: u32 = 0x0010_0000; pub const IMAGE_SCN_ALIGN_2BYTES: u32 = 0x0020_0000; pub const IMAGE_SCN_ALIGN_4BYTES: u32 = 0x0030_0000; pub const IMAGE_SCN_ALIGN_8BYTES: u32 = 0x0040_0000; /// Default alignment if no others are specified. pub const IMAGE_SCN_ALIGN_16BYTES: u32 = 0x0050_0000; pub const IMAGE_SCN_ALIGN_32BYTES: u32 = 0x0060_0000; pub const IMAGE_SCN_ALIGN_64BYTES: u32 = 0x0070_0000; pub const IMAGE_SCN_ALIGN_128BYTES: u32 = 0x0080_0000; pub const IMAGE_SCN_ALIGN_256BYTES: u32 = 0x0090_0000; pub const IMAGE_SCN_ALIGN_512BYTES: u32 = 0x00A0_0000; pub const IMAGE_SCN_ALIGN_1024BYTES: u32 = 0x00B0_0000; pub const IMAGE_SCN_ALIGN_2048BYTES: u32 = 0x00C0_0000; pub const IMAGE_SCN_ALIGN_4096BYTES: u32 = 0x00D0_0000; pub const IMAGE_SCN_ALIGN_8192BYTES: u32 = 0x00E0_0000; // Unused 0x00F0_0000 pub const IMAGE_SCN_ALIGN_MASK: u32 = 0x00F0_0000; /// Section contains extended relocations. pub const IMAGE_SCN_LNK_NRELOC_OVFL: u32 = 0x0100_0000; /// Section can be discarded. pub const IMAGE_SCN_MEM_DISCARDABLE: u32 = 0x0200_0000; /// Section is not cacheable. pub const IMAGE_SCN_MEM_NOT_CACHED: u32 = 0x0400_0000; /// Section is not pageable. pub const IMAGE_SCN_MEM_NOT_PAGED: u32 = 0x0800_0000; /// Section is shareable. pub const IMAGE_SCN_MEM_SHARED: u32 = 0x1000_0000; /// Section is executable. pub const IMAGE_SCN_MEM_EXECUTE: u32 = 0x2000_0000; /// Section is readable. pub const IMAGE_SCN_MEM_READ: u32 = 0x4000_0000; /// Section is writeable. pub const IMAGE_SCN_MEM_WRITE: u32 = 0x8000_0000; // // TLS Characteristic Flags // /// Tls index is scaled pub const IMAGE_SCN_SCALE_INDEX: u32 = 0x0000_0001; // // Symbol format. // // This struct has alignment 1. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageSymbol { /// If first 4 bytes are 0, then second 4 bytes are offset into string table. pub name: [u8; 8], pub value: U32Bytes, pub section_number: U16Bytes, pub typ: U16Bytes, pub storage_class: u8, pub number_of_aux_symbols: u8, } pub const IMAGE_SIZEOF_SYMBOL: usize = 18; #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageSymbolBytes(pub [u8; IMAGE_SIZEOF_SYMBOL]); // This struct has alignment 1. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageSymbolEx { /// If first 4 bytes are 0, then second 4 bytes are offset into string table. pub name: [u8; 8], pub value: U32Bytes, pub section_number: I32Bytes, pub typ: U16Bytes, pub storage_class: u8, pub number_of_aux_symbols: u8, } pub const IMAGE_SIZEOF_SYMBOL_EX: usize = 20; #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageSymbolExBytes(pub [u8; IMAGE_SIZEOF_SYMBOL_EX]); // Values for `ImageSymbol::section_number`. // // Symbols have a section number of the section in which they are // defined. Otherwise, section numbers have the following meanings: /// Symbol is undefined or is common. pub const IMAGE_SYM_UNDEFINED: i32 = 0; /// Symbol is an absolute value. pub const IMAGE_SYM_ABSOLUTE: i32 = -1; /// Symbol is a special debug item. pub const IMAGE_SYM_DEBUG: i32 = -2; /// Values 0xFF00-0xFFFF are special pub const IMAGE_SYM_SECTION_MAX: u16 = 0xFEFF; pub const IMAGE_SYM_SECTION_MAX_EX: u32 = 0x7fff_ffff; // Values for `ImageSymbol::typ` (basic component). /// no type. pub const IMAGE_SYM_TYPE_NULL: u16 = 0x0000; pub const IMAGE_SYM_TYPE_VOID: u16 = 0x0001; /// type character. pub const IMAGE_SYM_TYPE_CHAR: u16 = 0x0002; /// type short integer. pub const IMAGE_SYM_TYPE_SHORT: u16 = 0x0003; pub const IMAGE_SYM_TYPE_INT: u16 = 0x0004; pub const IMAGE_SYM_TYPE_LONG: u16 = 0x0005; pub const IMAGE_SYM_TYPE_FLOAT: u16 = 0x0006; pub const IMAGE_SYM_TYPE_DOUBLE: u16 = 0x0007; pub const IMAGE_SYM_TYPE_STRUCT: u16 = 0x0008; pub const IMAGE_SYM_TYPE_UNION: u16 = 0x0009; /// enumeration. pub const IMAGE_SYM_TYPE_ENUM: u16 = 0x000A; /// member of enumeration. pub const IMAGE_SYM_TYPE_MOE: u16 = 0x000B; pub const IMAGE_SYM_TYPE_BYTE: u16 = 0x000C; pub const IMAGE_SYM_TYPE_WORD: u16 = 0x000D; pub const IMAGE_SYM_TYPE_UINT: u16 = 0x000E; pub const IMAGE_SYM_TYPE_DWORD: u16 = 0x000F; pub const IMAGE_SYM_TYPE_PCODE: u16 = 0x8000; // Values for `ImageSymbol::typ` (derived component). /// no derived type. pub const IMAGE_SYM_DTYPE_NULL: u16 = 0; /// pointer. pub const IMAGE_SYM_DTYPE_POINTER: u16 = 1; /// function. pub const IMAGE_SYM_DTYPE_FUNCTION: u16 = 2; /// array. pub const IMAGE_SYM_DTYPE_ARRAY: u16 = 3; // Values for `ImageSymbol::storage_class`. pub const IMAGE_SYM_CLASS_END_OF_FUNCTION: u8 = 0xff; pub const IMAGE_SYM_CLASS_NULL: u8 = 0x00; pub const IMAGE_SYM_CLASS_AUTOMATIC: u8 = 0x01; pub const IMAGE_SYM_CLASS_EXTERNAL: u8 = 0x02; pub const IMAGE_SYM_CLASS_STATIC: u8 = 0x03; pub const IMAGE_SYM_CLASS_REGISTER: u8 = 0x04; pub const IMAGE_SYM_CLASS_EXTERNAL_DEF: u8 = 0x05; pub const IMAGE_SYM_CLASS_LABEL: u8 = 0x06; pub const IMAGE_SYM_CLASS_UNDEFINED_LABEL: u8 = 0x07; pub const IMAGE_SYM_CLASS_MEMBER_OF_STRUCT: u8 = 0x08; pub const IMAGE_SYM_CLASS_ARGUMENT: u8 = 0x09; pub const IMAGE_SYM_CLASS_STRUCT_TAG: u8 = 0x0A; pub const IMAGE_SYM_CLASS_MEMBER_OF_UNION: u8 = 0x0B; pub const IMAGE_SYM_CLASS_UNION_TAG: u8 = 0x0C; pub const IMAGE_SYM_CLASS_TYPE_DEFINITION: u8 = 0x0D; pub const IMAGE_SYM_CLASS_UNDEFINED_STATIC: u8 = 0x0E; pub const IMAGE_SYM_CLASS_ENUM_TAG: u8 = 0x0F; pub const IMAGE_SYM_CLASS_MEMBER_OF_ENUM: u8 = 0x10; pub const IMAGE_SYM_CLASS_REGISTER_PARAM: u8 = 0x11; pub const IMAGE_SYM_CLASS_BIT_FIELD: u8 = 0x12; pub const IMAGE_SYM_CLASS_FAR_EXTERNAL: u8 = 0x44; pub const IMAGE_SYM_CLASS_BLOCK: u8 = 0x64; pub const IMAGE_SYM_CLASS_FUNCTION: u8 = 0x65; pub const IMAGE_SYM_CLASS_END_OF_STRUCT: u8 = 0x66; pub const IMAGE_SYM_CLASS_FILE: u8 = 0x67; // new pub const IMAGE_SYM_CLASS_SECTION: u8 = 0x68; pub const IMAGE_SYM_CLASS_WEAK_EXTERNAL: u8 = 0x69; pub const IMAGE_SYM_CLASS_CLR_TOKEN: u8 = 0x6B; // type packing constants pub const N_BTMASK: u16 = 0x000F; pub const N_TMASK: u16 = 0x0030; pub const N_TMASK1: u16 = 0x00C0; pub const N_TMASK2: u16 = 0x00F0; pub const N_BTSHFT: usize = 4; pub const N_TSHIFT: usize = 2; pub const IMAGE_SYM_DTYPE_SHIFT: usize = N_BTSHFT; // // Auxiliary entry format. // // Used for both ImageSymbol and ImageSymbolEx (with padding). // This struct has alignment 1. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageAuxSymbolTokenDef { /// IMAGE_AUX_SYMBOL_TYPE pub aux_type: u8, /// Must be 0 pub reserved1: u8, pub symbol_table_index: U32Bytes, /// Must be 0 pub reserved2: [u8; 12], } pub const IMAGE_AUX_SYMBOL_TYPE_TOKEN_DEF: u16 = 1; /// Auxiliary symbol format 1: function definitions. // This struct has alignment 1. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageAuxSymbolFunction { pub tag_index: U32Bytes, pub total_size: U32Bytes, pub pointer_to_linenumber: U32Bytes, pub pointer_to_next_function: U32Bytes, pub unused: [u8; 2], } /// Auxiliary symbol format 2: .bf and .ef symbols. // This struct has alignment 1. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageAuxSymbolFunctionBeginEnd { pub unused1: [u8; 4], /// declaration line number pub linenumber: U16Bytes, pub unused2: [u8; 6], pub pointer_to_next_function: U32Bytes, pub unused3: [u8; 2], } /// Auxiliary symbol format 3: weak externals. /// /// Used for both `ImageSymbol` and `ImageSymbolEx` (both with padding). // This struct has alignment 1. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageAuxSymbolWeak { /// the weak extern default symbol index pub weak_default_sym_index: U32Bytes, pub weak_search_type: U32Bytes, } /// Auxiliary symbol format 5: sections. /// /// Used for both `ImageSymbol` and `ImageSymbolEx` (with padding). // This struct has alignment 1. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageAuxSymbolSection { /// section length pub length: U32Bytes, /// number of relocation entries pub number_of_relocations: U16Bytes, /// number of line numbers pub number_of_linenumbers: U16Bytes, /// checksum for communal pub check_sum: U32Bytes, /// section number to associate with pub number: U16Bytes, /// communal selection type pub selection: u8, pub reserved: u8, /// high bits of the section number pub high_number: U16Bytes, } // Used for both ImageSymbol and ImageSymbolEx (both with padding). // This struct has alignment 1. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageAuxSymbolCrc { pub crc: U32Bytes, } // // Communal selection types. // pub const IMAGE_COMDAT_SELECT_NODUPLICATES: u8 = 1; pub const IMAGE_COMDAT_SELECT_ANY: u8 = 2; pub const IMAGE_COMDAT_SELECT_SAME_SIZE: u8 = 3; pub const IMAGE_COMDAT_SELECT_EXACT_MATCH: u8 = 4; pub const IMAGE_COMDAT_SELECT_ASSOCIATIVE: u8 = 5; pub const IMAGE_COMDAT_SELECT_LARGEST: u8 = 6; pub const IMAGE_COMDAT_SELECT_NEWEST: u8 = 7; pub const IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY: u16 = 1; pub const IMAGE_WEAK_EXTERN_SEARCH_LIBRARY: u16 = 2; pub const IMAGE_WEAK_EXTERN_SEARCH_ALIAS: u16 = 3; pub const IMAGE_WEAK_EXTERN_ANTI_DEPENDENCY: u16 = 4; // // Relocation format. // // This struct has alignment 1. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageRelocation { /// Also `RelocCount` when IMAGE_SCN_LNK_NRELOC_OVFL is set pub virtual_address: U32Bytes, pub symbol_table_index: U32Bytes, pub typ: U16Bytes, } // // I386 relocation types. // /// Reference is absolute, no relocation is necessary pub const IMAGE_REL_I386_ABSOLUTE: u16 = 0x0000; /// Direct 16-bit reference to the symbols virtual address pub const IMAGE_REL_I386_DIR16: u16 = 0x0001; /// PC-relative 16-bit reference to the symbols virtual address pub const IMAGE_REL_I386_REL16: u16 = 0x0002; /// Direct 32-bit reference to the symbols virtual address pub const IMAGE_REL_I386_DIR32: u16 = 0x0006; /// Direct 32-bit reference to the symbols virtual address, base not included pub const IMAGE_REL_I386_DIR32NB: u16 = 0x0007; /// Direct 16-bit reference to the segment-selector bits of a 32-bit virtual address pub const IMAGE_REL_I386_SEG12: u16 = 0x0009; pub const IMAGE_REL_I386_SECTION: u16 = 0x000A; pub const IMAGE_REL_I386_SECREL: u16 = 0x000B; /// clr token pub const IMAGE_REL_I386_TOKEN: u16 = 0x000C; /// 7 bit offset from base of section containing target pub const IMAGE_REL_I386_SECREL7: u16 = 0x000D; /// PC-relative 32-bit reference to the symbols virtual address pub const IMAGE_REL_I386_REL32: u16 = 0x0014; // // MIPS relocation types. // /// Reference is absolute, no relocation is necessary pub const IMAGE_REL_MIPS_ABSOLUTE: u16 = 0x0000; pub const IMAGE_REL_MIPS_REFHALF: u16 = 0x0001; pub const IMAGE_REL_MIPS_REFWORD: u16 = 0x0002; pub const IMAGE_REL_MIPS_JMPADDR: u16 = 0x0003; pub const IMAGE_REL_MIPS_REFHI: u16 = 0x0004; pub const IMAGE_REL_MIPS_REFLO: u16 = 0x0005; pub const IMAGE_REL_MIPS_GPREL: u16 = 0x0006; pub const IMAGE_REL_MIPS_LITERAL: u16 = 0x0007; pub const IMAGE_REL_MIPS_SECTION: u16 = 0x000A; pub const IMAGE_REL_MIPS_SECREL: u16 = 0x000B; /// Low 16-bit section relative reference (used for >32k TLS) pub const IMAGE_REL_MIPS_SECRELLO: u16 = 0x000C; /// High 16-bit section relative reference (used for >32k TLS) pub const IMAGE_REL_MIPS_SECRELHI: u16 = 0x000D; /// clr token pub const IMAGE_REL_MIPS_TOKEN: u16 = 0x000E; pub const IMAGE_REL_MIPS_JMPADDR16: u16 = 0x0010; pub const IMAGE_REL_MIPS_REFWORDNB: u16 = 0x0022; pub const IMAGE_REL_MIPS_PAIR: u16 = 0x0025; // // Alpha Relocation types. // pub const IMAGE_REL_ALPHA_ABSOLUTE: u16 = 0x0000; pub const IMAGE_REL_ALPHA_REFLONG: u16 = 0x0001; pub const IMAGE_REL_ALPHA_REFQUAD: u16 = 0x0002; pub const IMAGE_REL_ALPHA_GPREL32: u16 = 0x0003; pub const IMAGE_REL_ALPHA_LITERAL: u16 = 0x0004; pub const IMAGE_REL_ALPHA_LITUSE: u16 = 0x0005; pub const IMAGE_REL_ALPHA_GPDISP: u16 = 0x0006; pub const IMAGE_REL_ALPHA_BRADDR: u16 = 0x0007; pub const IMAGE_REL_ALPHA_HINT: u16 = 0x0008; pub const IMAGE_REL_ALPHA_INLINE_REFLONG: u16 = 0x0009; pub const IMAGE_REL_ALPHA_REFHI: u16 = 0x000A; pub const IMAGE_REL_ALPHA_REFLO: u16 = 0x000B; pub const IMAGE_REL_ALPHA_PAIR: u16 = 0x000C; pub const IMAGE_REL_ALPHA_MATCH: u16 = 0x000D; pub const IMAGE_REL_ALPHA_SECTION: u16 = 0x000E; pub const IMAGE_REL_ALPHA_SECREL: u16 = 0x000F; pub const IMAGE_REL_ALPHA_REFLONGNB: u16 = 0x0010; /// Low 16-bit section relative reference pub const IMAGE_REL_ALPHA_SECRELLO: u16 = 0x0011; /// High 16-bit section relative reference pub const IMAGE_REL_ALPHA_SECRELHI: u16 = 0x0012; /// High 16 bits of 48 bit reference pub const IMAGE_REL_ALPHA_REFQ3: u16 = 0x0013; /// Middle 16 bits of 48 bit reference pub const IMAGE_REL_ALPHA_REFQ2: u16 = 0x0014; /// Low 16 bits of 48 bit reference pub const IMAGE_REL_ALPHA_REFQ1: u16 = 0x0015; /// Low 16-bit GP relative reference pub const IMAGE_REL_ALPHA_GPRELLO: u16 = 0x0016; /// High 16-bit GP relative reference pub const IMAGE_REL_ALPHA_GPRELHI: u16 = 0x0017; // // IBM PowerPC relocation types. // /// NOP pub const IMAGE_REL_PPC_ABSOLUTE: u16 = 0x0000; /// 64-bit address pub const IMAGE_REL_PPC_ADDR64: u16 = 0x0001; /// 32-bit address pub const IMAGE_REL_PPC_ADDR32: u16 = 0x0002; /// 26-bit address, shifted left 2 (branch absolute) pub const IMAGE_REL_PPC_ADDR24: u16 = 0x0003; /// 16-bit address pub const IMAGE_REL_PPC_ADDR16: u16 = 0x0004; /// 16-bit address, shifted left 2 (load doubleword) pub const IMAGE_REL_PPC_ADDR14: u16 = 0x0005; /// 26-bit PC-relative offset, shifted left 2 (branch relative) pub const IMAGE_REL_PPC_REL24: u16 = 0x0006; /// 16-bit PC-relative offset, shifted left 2 (br cond relative) pub const IMAGE_REL_PPC_REL14: u16 = 0x0007; /// 16-bit offset from TOC base pub const IMAGE_REL_PPC_TOCREL16: u16 = 0x0008; /// 16-bit offset from TOC base, shifted left 2 (load doubleword) pub const IMAGE_REL_PPC_TOCREL14: u16 = 0x0009; /// 32-bit addr w/o image base pub const IMAGE_REL_PPC_ADDR32NB: u16 = 0x000A; /// va of containing section (as in an image sectionhdr) pub const IMAGE_REL_PPC_SECREL: u16 = 0x000B; /// sectionheader number pub const IMAGE_REL_PPC_SECTION: u16 = 0x000C; /// substitute TOC restore instruction iff symbol is glue code pub const IMAGE_REL_PPC_IFGLUE: u16 = 0x000D; /// symbol is glue code; virtual address is TOC restore instruction pub const IMAGE_REL_PPC_IMGLUE: u16 = 0x000E; /// va of containing section (limited to 16 bits) pub const IMAGE_REL_PPC_SECREL16: u16 = 0x000F; pub const IMAGE_REL_PPC_REFHI: u16 = 0x0010; pub const IMAGE_REL_PPC_REFLO: u16 = 0x0011; pub const IMAGE_REL_PPC_PAIR: u16 = 0x0012; /// Low 16-bit section relative reference (used for >32k TLS) pub const IMAGE_REL_PPC_SECRELLO: u16 = 0x0013; /// High 16-bit section relative reference (used for >32k TLS) pub const IMAGE_REL_PPC_SECRELHI: u16 = 0x0014; pub const IMAGE_REL_PPC_GPREL: u16 = 0x0015; /// clr token pub const IMAGE_REL_PPC_TOKEN: u16 = 0x0016; /// mask to isolate above values in IMAGE_RELOCATION.Type pub const IMAGE_REL_PPC_TYPEMASK: u16 = 0x00FF; // Flag bits in `ImageRelocation::typ`. /// subtract reloc value rather than adding it pub const IMAGE_REL_PPC_NEG: u16 = 0x0100; /// fix branch prediction bit to predict branch taken pub const IMAGE_REL_PPC_BRTAKEN: u16 = 0x0200; /// fix branch prediction bit to predict branch not taken pub const IMAGE_REL_PPC_BRNTAKEN: u16 = 0x0400; /// toc slot defined in file (or, data in toc) pub const IMAGE_REL_PPC_TOCDEFN: u16 = 0x0800; // // Hitachi SH3 relocation types. // /// No relocation pub const IMAGE_REL_SH3_ABSOLUTE: u16 = 0x0000; /// 16 bit direct pub const IMAGE_REL_SH3_DIRECT16: u16 = 0x0001; /// 32 bit direct pub const IMAGE_REL_SH3_DIRECT32: u16 = 0x0002; /// 8 bit direct, -128..255 pub const IMAGE_REL_SH3_DIRECT8: u16 = 0x0003; /// 8 bit direct .W (0 ext.) pub const IMAGE_REL_SH3_DIRECT8_WORD: u16 = 0x0004; /// 8 bit direct .L (0 ext.) pub const IMAGE_REL_SH3_DIRECT8_LONG: u16 = 0x0005; /// 4 bit direct (0 ext.) pub const IMAGE_REL_SH3_DIRECT4: u16 = 0x0006; /// 4 bit direct .W (0 ext.) pub const IMAGE_REL_SH3_DIRECT4_WORD: u16 = 0x0007; /// 4 bit direct .L (0 ext.) pub const IMAGE_REL_SH3_DIRECT4_LONG: u16 = 0x0008; /// 8 bit PC relative .W pub const IMAGE_REL_SH3_PCREL8_WORD: u16 = 0x0009; /// 8 bit PC relative .L pub const IMAGE_REL_SH3_PCREL8_LONG: u16 = 0x000A; /// 12 LSB PC relative .W pub const IMAGE_REL_SH3_PCREL12_WORD: u16 = 0x000B; /// Start of EXE section pub const IMAGE_REL_SH3_STARTOF_SECTION: u16 = 0x000C; /// Size of EXE section pub const IMAGE_REL_SH3_SIZEOF_SECTION: u16 = 0x000D; /// Section table index pub const IMAGE_REL_SH3_SECTION: u16 = 0x000E; /// Offset within section pub const IMAGE_REL_SH3_SECREL: u16 = 0x000F; /// 32 bit direct not based pub const IMAGE_REL_SH3_DIRECT32_NB: u16 = 0x0010; /// GP-relative addressing pub const IMAGE_REL_SH3_GPREL4_LONG: u16 = 0x0011; /// clr token pub const IMAGE_REL_SH3_TOKEN: u16 = 0x0012; /// Offset from current instruction in longwords /// if not NOMODE, insert the inverse of the low bit at bit 32 to select PTA/PTB pub const IMAGE_REL_SHM_PCRELPT: u16 = 0x0013; /// Low bits of 32-bit address pub const IMAGE_REL_SHM_REFLO: u16 = 0x0014; /// High bits of 32-bit address pub const IMAGE_REL_SHM_REFHALF: u16 = 0x0015; /// Low bits of relative reference pub const IMAGE_REL_SHM_RELLO: u16 = 0x0016; /// High bits of relative reference pub const IMAGE_REL_SHM_RELHALF: u16 = 0x0017; /// offset operand for relocation pub const IMAGE_REL_SHM_PAIR: u16 = 0x0018; /// relocation ignores section mode pub const IMAGE_REL_SH_NOMODE: u16 = 0x8000; /// No relocation required pub const IMAGE_REL_ARM_ABSOLUTE: u16 = 0x0000; /// 32 bit address pub const IMAGE_REL_ARM_ADDR32: u16 = 0x0001; /// 32 bit address w/o image base pub const IMAGE_REL_ARM_ADDR32NB: u16 = 0x0002; /// 24 bit offset << 2 & sign ext. pub const IMAGE_REL_ARM_BRANCH24: u16 = 0x0003; /// Thumb: 2 11 bit offsets pub const IMAGE_REL_ARM_BRANCH11: u16 = 0x0004; /// clr token pub const IMAGE_REL_ARM_TOKEN: u16 = 0x0005; /// GP-relative addressing (ARM) pub const IMAGE_REL_ARM_GPREL12: u16 = 0x0006; /// GP-relative addressing (Thumb) pub const IMAGE_REL_ARM_GPREL7: u16 = 0x0007; pub const IMAGE_REL_ARM_BLX24: u16 = 0x0008; pub const IMAGE_REL_ARM_BLX11: u16 = 0x0009; /// 32-bit relative address from byte following reloc pub const IMAGE_REL_ARM_REL32: u16 = 0x000A; /// Section table index pub const IMAGE_REL_ARM_SECTION: u16 = 0x000E; /// Offset within section pub const IMAGE_REL_ARM_SECREL: u16 = 0x000F; /// ARM: MOVW/MOVT pub const IMAGE_REL_ARM_MOV32A: u16 = 0x0010; /// ARM: MOVW/MOVT (deprecated) pub const IMAGE_REL_ARM_MOV32: u16 = 0x0010; /// Thumb: MOVW/MOVT pub const IMAGE_REL_ARM_MOV32T: u16 = 0x0011; /// Thumb: MOVW/MOVT (deprecated) pub const IMAGE_REL_THUMB_MOV32: u16 = 0x0011; /// Thumb: 32-bit conditional B pub const IMAGE_REL_ARM_BRANCH20T: u16 = 0x0012; /// Thumb: 32-bit conditional B (deprecated) pub const IMAGE_REL_THUMB_BRANCH20: u16 = 0x0012; /// Thumb: 32-bit B or BL pub const IMAGE_REL_ARM_BRANCH24T: u16 = 0x0014; /// Thumb: 32-bit B or BL (deprecated) pub const IMAGE_REL_THUMB_BRANCH24: u16 = 0x0014; /// Thumb: BLX immediate pub const IMAGE_REL_ARM_BLX23T: u16 = 0x0015; /// Thumb: BLX immediate (deprecated) pub const IMAGE_REL_THUMB_BLX23: u16 = 0x0015; pub const IMAGE_REL_AM_ABSOLUTE: u16 = 0x0000; pub const IMAGE_REL_AM_ADDR32: u16 = 0x0001; pub const IMAGE_REL_AM_ADDR32NB: u16 = 0x0002; pub const IMAGE_REL_AM_CALL32: u16 = 0x0003; pub const IMAGE_REL_AM_FUNCINFO: u16 = 0x0004; pub const IMAGE_REL_AM_REL32_1: u16 = 0x0005; pub const IMAGE_REL_AM_REL32_2: u16 = 0x0006; pub const IMAGE_REL_AM_SECREL: u16 = 0x0007; pub const IMAGE_REL_AM_SECTION: u16 = 0x0008; pub const IMAGE_REL_AM_TOKEN: u16 = 0x0009; // // ARM64 relocations types. // /// No relocation required pub const IMAGE_REL_ARM64_ABSOLUTE: u16 = 0x0000; /// 32 bit address. Review! do we need it? pub const IMAGE_REL_ARM64_ADDR32: u16 = 0x0001; /// 32 bit address w/o image base (RVA: for Data/PData/XData) pub const IMAGE_REL_ARM64_ADDR32NB: u16 = 0x0002; /// 26 bit offset << 2 & sign ext. for B & BL pub const IMAGE_REL_ARM64_BRANCH26: u16 = 0x0003; /// ADRP pub const IMAGE_REL_ARM64_PAGEBASE_REL21: u16 = 0x0004; /// ADR pub const IMAGE_REL_ARM64_REL21: u16 = 0x0005; /// ADD/ADDS (immediate) with zero shift, for page offset pub const IMAGE_REL_ARM64_PAGEOFFSET_12A: u16 = 0x0006; /// LDR (indexed, unsigned immediate), for page offset pub const IMAGE_REL_ARM64_PAGEOFFSET_12L: u16 = 0x0007; /// Offset within section pub const IMAGE_REL_ARM64_SECREL: u16 = 0x0008; /// ADD/ADDS (immediate) with zero shift, for bit 0:11 of section offset pub const IMAGE_REL_ARM64_SECREL_LOW12A: u16 = 0x0009; /// ADD/ADDS (immediate) with zero shift, for bit 12:23 of section offset pub const IMAGE_REL_ARM64_SECREL_HIGH12A: u16 = 0x000A; /// LDR (indexed, unsigned immediate), for bit 0:11 of section offset pub const IMAGE_REL_ARM64_SECREL_LOW12L: u16 = 0x000B; pub const IMAGE_REL_ARM64_TOKEN: u16 = 0x000C; /// Section table index pub const IMAGE_REL_ARM64_SECTION: u16 = 0x000D; /// 64 bit address pub const IMAGE_REL_ARM64_ADDR64: u16 = 0x000E; /// 19 bit offset << 2 & sign ext. for conditional B pub const IMAGE_REL_ARM64_BRANCH19: u16 = 0x000F; /// TBZ/TBNZ pub const IMAGE_REL_ARM64_BRANCH14: u16 = 0x0010; /// 32-bit relative address from byte following reloc pub const IMAGE_REL_ARM64_REL32: u16 = 0x0011; // // x64 relocations // /// Reference is absolute, no relocation is necessary pub const IMAGE_REL_AMD64_ABSOLUTE: u16 = 0x0000; /// 64-bit address (VA). pub const IMAGE_REL_AMD64_ADDR64: u16 = 0x0001; /// 32-bit address (VA). pub const IMAGE_REL_AMD64_ADDR32: u16 = 0x0002; /// 32-bit address w/o image base (RVA). pub const IMAGE_REL_AMD64_ADDR32NB: u16 = 0x0003; /// 32-bit relative address from byte following reloc pub const IMAGE_REL_AMD64_REL32: u16 = 0x0004; /// 32-bit relative address from byte distance 1 from reloc pub const IMAGE_REL_AMD64_REL32_1: u16 = 0x0005; /// 32-bit relative address from byte distance 2 from reloc pub const IMAGE_REL_AMD64_REL32_2: u16 = 0x0006; /// 32-bit relative address from byte distance 3 from reloc pub const IMAGE_REL_AMD64_REL32_3: u16 = 0x0007; /// 32-bit relative address from byte distance 4 from reloc pub const IMAGE_REL_AMD64_REL32_4: u16 = 0x0008; /// 32-bit relative address from byte distance 5 from reloc pub const IMAGE_REL_AMD64_REL32_5: u16 = 0x0009; /// Section index pub const IMAGE_REL_AMD64_SECTION: u16 = 0x000A; /// 32 bit offset from base of section containing target pub const IMAGE_REL_AMD64_SECREL: u16 = 0x000B; /// 7 bit unsigned offset from base of section containing target pub const IMAGE_REL_AMD64_SECREL7: u16 = 0x000C; /// 32 bit metadata token pub const IMAGE_REL_AMD64_TOKEN: u16 = 0x000D; /// 32 bit signed span-dependent value emitted into object pub const IMAGE_REL_AMD64_SREL32: u16 = 0x000E; pub const IMAGE_REL_AMD64_PAIR: u16 = 0x000F; /// 32 bit signed span-dependent value applied at link time pub const IMAGE_REL_AMD64_SSPAN32: u16 = 0x0010; pub const IMAGE_REL_AMD64_EHANDLER: u16 = 0x0011; /// Indirect branch to an import pub const IMAGE_REL_AMD64_IMPORT_BR: u16 = 0x0012; /// Indirect call to an import pub const IMAGE_REL_AMD64_IMPORT_CALL: u16 = 0x0013; /// Indirect branch to a CFG check pub const IMAGE_REL_AMD64_CFG_BR: u16 = 0x0014; /// Indirect branch to a CFG check, with REX.W prefix pub const IMAGE_REL_AMD64_CFG_BR_REX: u16 = 0x0015; /// Indirect call to a CFG check pub const IMAGE_REL_AMD64_CFG_CALL: u16 = 0x0016; /// Indirect branch to a target in RAX (no CFG) pub const IMAGE_REL_AMD64_INDIR_BR: u16 = 0x0017; /// Indirect branch to a target in RAX, with REX.W prefix (no CFG) pub const IMAGE_REL_AMD64_INDIR_BR_REX: u16 = 0x0018; /// Indirect call to a target in RAX (no CFG) pub const IMAGE_REL_AMD64_INDIR_CALL: u16 = 0x0019; /// Indirect branch for a switch table using Reg 0 (RAX) pub const IMAGE_REL_AMD64_INDIR_BR_SWITCHTABLE_FIRST: u16 = 0x0020; /// Indirect branch for a switch table using Reg 15 (R15) pub const IMAGE_REL_AMD64_INDIR_BR_SWITCHTABLE_LAST: u16 = 0x002F; // // IA64 relocation types. // pub const IMAGE_REL_IA64_ABSOLUTE: u16 = 0x0000; pub const IMAGE_REL_IA64_IMM14: u16 = 0x0001; pub const IMAGE_REL_IA64_IMM22: u16 = 0x0002; pub const IMAGE_REL_IA64_IMM64: u16 = 0x0003; pub const IMAGE_REL_IA64_DIR32: u16 = 0x0004; pub const IMAGE_REL_IA64_DIR64: u16 = 0x0005; pub const IMAGE_REL_IA64_PCREL21B: u16 = 0x0006; pub const IMAGE_REL_IA64_PCREL21M: u16 = 0x0007; pub const IMAGE_REL_IA64_PCREL21F: u16 = 0x0008; pub const IMAGE_REL_IA64_GPREL22: u16 = 0x0009; pub const IMAGE_REL_IA64_LTOFF22: u16 = 0x000A; pub const IMAGE_REL_IA64_SECTION: u16 = 0x000B; pub const IMAGE_REL_IA64_SECREL22: u16 = 0x000C; pub const IMAGE_REL_IA64_SECREL64I: u16 = 0x000D; pub const IMAGE_REL_IA64_SECREL32: u16 = 0x000E; // pub const IMAGE_REL_IA64_DIR32NB: u16 = 0x0010; pub const IMAGE_REL_IA64_SREL14: u16 = 0x0011; pub const IMAGE_REL_IA64_SREL22: u16 = 0x0012; pub const IMAGE_REL_IA64_SREL32: u16 = 0x0013; pub const IMAGE_REL_IA64_UREL32: u16 = 0x0014; /// This is always a BRL and never converted pub const IMAGE_REL_IA64_PCREL60X: u16 = 0x0015; /// If possible, convert to MBB bundle with NOP.B in slot 1 pub const IMAGE_REL_IA64_PCREL60B: u16 = 0x0016; /// If possible, convert to MFB bundle with NOP.F in slot 1 pub const IMAGE_REL_IA64_PCREL60F: u16 = 0x0017; /// If possible, convert to MIB bundle with NOP.I in slot 1 pub const IMAGE_REL_IA64_PCREL60I: u16 = 0x0018; /// If possible, convert to MMB bundle with NOP.M in slot 1 pub const IMAGE_REL_IA64_PCREL60M: u16 = 0x0019; pub const IMAGE_REL_IA64_IMMGPREL64: u16 = 0x001A; /// clr token pub const IMAGE_REL_IA64_TOKEN: u16 = 0x001B; pub const IMAGE_REL_IA64_GPREL32: u16 = 0x001C; pub const IMAGE_REL_IA64_ADDEND: u16 = 0x001F; // // CEF relocation types. // /// Reference is absolute, no relocation is necessary pub const IMAGE_REL_CEF_ABSOLUTE: u16 = 0x0000; /// 32-bit address (VA). pub const IMAGE_REL_CEF_ADDR32: u16 = 0x0001; /// 64-bit address (VA). pub const IMAGE_REL_CEF_ADDR64: u16 = 0x0002; /// 32-bit address w/o image base (RVA). pub const IMAGE_REL_CEF_ADDR32NB: u16 = 0x0003; /// Section index pub const IMAGE_REL_CEF_SECTION: u16 = 0x0004; /// 32 bit offset from base of section containing target pub const IMAGE_REL_CEF_SECREL: u16 = 0x0005; /// 32 bit metadata token pub const IMAGE_REL_CEF_TOKEN: u16 = 0x0006; // // clr relocation types. // /// Reference is absolute, no relocation is necessary pub const IMAGE_REL_CEE_ABSOLUTE: u16 = 0x0000; /// 32-bit address (VA). pub const IMAGE_REL_CEE_ADDR32: u16 = 0x0001; /// 64-bit address (VA). pub const IMAGE_REL_CEE_ADDR64: u16 = 0x0002; /// 32-bit address w/o image base (RVA). pub const IMAGE_REL_CEE_ADDR32NB: u16 = 0x0003; /// Section index pub const IMAGE_REL_CEE_SECTION: u16 = 0x0004; /// 32 bit offset from base of section containing target pub const IMAGE_REL_CEE_SECREL: u16 = 0x0005; /// 32 bit metadata token pub const IMAGE_REL_CEE_TOKEN: u16 = 0x0006; /// No relocation required pub const IMAGE_REL_M32R_ABSOLUTE: u16 = 0x0000; /// 32 bit address pub const IMAGE_REL_M32R_ADDR32: u16 = 0x0001; /// 32 bit address w/o image base pub const IMAGE_REL_M32R_ADDR32NB: u16 = 0x0002; /// 24 bit address pub const IMAGE_REL_M32R_ADDR24: u16 = 0x0003; /// GP relative addressing pub const IMAGE_REL_M32R_GPREL16: u16 = 0x0004; /// 24 bit offset << 2 & sign ext. pub const IMAGE_REL_M32R_PCREL24: u16 = 0x0005; /// 16 bit offset << 2 & sign ext. pub const IMAGE_REL_M32R_PCREL16: u16 = 0x0006; /// 8 bit offset << 2 & sign ext. pub const IMAGE_REL_M32R_PCREL8: u16 = 0x0007; /// 16 MSBs pub const IMAGE_REL_M32R_REFHALF: u16 = 0x0008; /// 16 MSBs; adj for LSB sign ext. pub const IMAGE_REL_M32R_REFHI: u16 = 0x0009; /// 16 LSBs pub const IMAGE_REL_M32R_REFLO: u16 = 0x000A; /// Link HI and LO pub const IMAGE_REL_M32R_PAIR: u16 = 0x000B; /// Section table index pub const IMAGE_REL_M32R_SECTION: u16 = 0x000C; /// 32 bit section relative reference pub const IMAGE_REL_M32R_SECREL32: u16 = 0x000D; /// clr token pub const IMAGE_REL_M32R_TOKEN: u16 = 0x000E; /// No relocation required pub const IMAGE_REL_EBC_ABSOLUTE: u16 = 0x0000; /// 32 bit address w/o image base pub const IMAGE_REL_EBC_ADDR32NB: u16 = 0x0001; /// 32-bit relative address from byte following reloc pub const IMAGE_REL_EBC_REL32: u16 = 0x0002; /// Section table index pub const IMAGE_REL_EBC_SECTION: u16 = 0x0003; /// Offset within section pub const IMAGE_REL_EBC_SECREL: u16 = 0x0004; /* // TODO? #define EXT_IMM64(Value, Address, Size, InstPos, ValPos) /* Intel-IA64-Filler */ \ Value |= (((ULONGLONG)((*(Address) >> InstPos) & (((ULONGLONG)1 << Size) - 1))) << ValPos) // Intel-IA64-Filler #define INS_IMM64(Value, Address, Size, InstPos, ValPos) /* Intel-IA64-Filler */\ *(PDWORD)Address = (*(PDWORD)Address & ~(((1 << Size) - 1) << InstPos)) | /* Intel-IA64-Filler */\ ((DWORD)((((ULONGLONG)Value >> ValPos) & (((ULONGLONG)1 << Size) - 1))) << InstPos) // Intel-IA64-Filler */ /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM7B_INST_WORD_X: u16 = 3; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM7B_SIZE_X: u16 = 7; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM7B_INST_WORD_POS_X: u16 = 4; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM7B_VAL_POS_X: u16 = 0; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM9D_INST_WORD_X: u16 = 3; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM9D_SIZE_X: u16 = 9; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM9D_INST_WORD_POS_X: u16 = 18; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM9D_VAL_POS_X: u16 = 7; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM5C_INST_WORD_X: u16 = 3; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM5C_SIZE_X: u16 = 5; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM5C_INST_WORD_POS_X: u16 = 13; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM5C_VAL_POS_X: u16 = 16; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IC_INST_WORD_X: u16 = 3; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IC_SIZE_X: u16 = 1; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IC_INST_WORD_POS_X: u16 = 12; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IC_VAL_POS_X: u16 = 21; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM41A_INST_WORD_X: u16 = 1; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM41A_SIZE_X: u16 = 10; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM41A_INST_WORD_POS_X: u16 = 14; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM41A_VAL_POS_X: u16 = 22; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM41B_INST_WORD_X: u16 = 1; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM41B_SIZE_X: u16 = 8; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM41B_INST_WORD_POS_X: u16 = 24; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM41B_VAL_POS_X: u16 = 32; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM41C_INST_WORD_X: u16 = 2; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM41C_SIZE_X: u16 = 23; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM41C_INST_WORD_POS_X: u16 = 0; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_IMM41C_VAL_POS_X: u16 = 40; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_SIGN_INST_WORD_X: u16 = 3; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_SIGN_SIZE_X: u16 = 1; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_SIGN_INST_WORD_POS_X: u16 = 27; /// Intel-IA64-Filler pub const EMARCH_ENC_I17_SIGN_VAL_POS_X: u16 = 63; /// Intel-IA64-Filler pub const X3_OPCODE_INST_WORD_X: u16 = 3; /// Intel-IA64-Filler pub const X3_OPCODE_SIZE_X: u16 = 4; /// Intel-IA64-Filler pub const X3_OPCODE_INST_WORD_POS_X: u16 = 28; /// Intel-IA64-Filler pub const X3_OPCODE_SIGN_VAL_POS_X: u16 = 0; /// Intel-IA64-Filler pub const X3_I_INST_WORD_X: u16 = 3; /// Intel-IA64-Filler pub const X3_I_SIZE_X: u16 = 1; /// Intel-IA64-Filler pub const X3_I_INST_WORD_POS_X: u16 = 27; /// Intel-IA64-Filler pub const X3_I_SIGN_VAL_POS_X: u16 = 59; /// Intel-IA64-Filler pub const X3_D_WH_INST_WORD_X: u16 = 3; /// Intel-IA64-Filler pub const X3_D_WH_SIZE_X: u16 = 3; /// Intel-IA64-Filler pub const X3_D_WH_INST_WORD_POS_X: u16 = 24; /// Intel-IA64-Filler pub const X3_D_WH_SIGN_VAL_POS_X: u16 = 0; /// Intel-IA64-Filler pub const X3_IMM20_INST_WORD_X: u16 = 3; /// Intel-IA64-Filler pub const X3_IMM20_SIZE_X: u16 = 20; /// Intel-IA64-Filler pub const X3_IMM20_INST_WORD_POS_X: u16 = 4; /// Intel-IA64-Filler pub const X3_IMM20_SIGN_VAL_POS_X: u16 = 0; /// Intel-IA64-Filler pub const X3_IMM39_1_INST_WORD_X: u16 = 2; /// Intel-IA64-Filler pub const X3_IMM39_1_SIZE_X: u16 = 23; /// Intel-IA64-Filler pub const X3_IMM39_1_INST_WORD_POS_X: u16 = 0; /// Intel-IA64-Filler pub const X3_IMM39_1_SIGN_VAL_POS_X: u16 = 36; /// Intel-IA64-Filler pub const X3_IMM39_2_INST_WORD_X: u16 = 1; /// Intel-IA64-Filler pub const X3_IMM39_2_SIZE_X: u16 = 16; /// Intel-IA64-Filler pub const X3_IMM39_2_INST_WORD_POS_X: u16 = 16; /// Intel-IA64-Filler pub const X3_IMM39_2_SIGN_VAL_POS_X: u16 = 20; /// Intel-IA64-Filler pub const X3_P_INST_WORD_X: u16 = 3; /// Intel-IA64-Filler pub const X3_P_SIZE_X: u16 = 4; /// Intel-IA64-Filler pub const X3_P_INST_WORD_POS_X: u16 = 0; /// Intel-IA64-Filler pub const X3_P_SIGN_VAL_POS_X: u16 = 0; /// Intel-IA64-Filler pub const X3_TMPLT_INST_WORD_X: u16 = 0; /// Intel-IA64-Filler pub const X3_TMPLT_SIZE_X: u16 = 4; /// Intel-IA64-Filler pub const X3_TMPLT_INST_WORD_POS_X: u16 = 0; /// Intel-IA64-Filler pub const X3_TMPLT_SIGN_VAL_POS_X: u16 = 0; /// Intel-IA64-Filler pub const X3_BTYPE_QP_INST_WORD_X: u16 = 2; /// Intel-IA64-Filler pub const X3_BTYPE_QP_SIZE_X: u16 = 9; /// Intel-IA64-Filler pub const X3_BTYPE_QP_INST_WORD_POS_X: u16 = 23; /// Intel-IA64-Filler pub const X3_BTYPE_QP_INST_VAL_POS_X: u16 = 0; /// Intel-IA64-Filler pub const X3_EMPTY_INST_WORD_X: u16 = 1; /// Intel-IA64-Filler pub const X3_EMPTY_SIZE_X: u16 = 2; /// Intel-IA64-Filler pub const X3_EMPTY_INST_WORD_POS_X: u16 = 14; /// Intel-IA64-Filler pub const X3_EMPTY_INST_VAL_POS_X: u16 = 0; // // Line number format. // // This struct has alignment 1. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageLinenumber { /// Symbol table index of function name if Linenumber is 0. /// Otherwise virtual address of line number. pub symbol_table_index_or_virtual_address: U32Bytes, /// Line number. pub linenumber: U16Bytes, } // // Based relocation format. // #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageBaseRelocation { pub virtual_address: U32, pub size_of_block: U32, // pub type_offset[1]: U16, } // // Based relocation types. // pub const IMAGE_REL_BASED_ABSOLUTE: u16 = 0; pub const IMAGE_REL_BASED_HIGH: u16 = 1; pub const IMAGE_REL_BASED_LOW: u16 = 2; pub const IMAGE_REL_BASED_HIGHLOW: u16 = 3; pub const IMAGE_REL_BASED_HIGHADJ: u16 = 4; pub const IMAGE_REL_BASED_MACHINE_SPECIFIC_5: u16 = 5; pub const IMAGE_REL_BASED_RESERVED: u16 = 6; pub const IMAGE_REL_BASED_MACHINE_SPECIFIC_7: u16 = 7; pub const IMAGE_REL_BASED_MACHINE_SPECIFIC_8: u16 = 8; pub const IMAGE_REL_BASED_MACHINE_SPECIFIC_9: u16 = 9; pub const IMAGE_REL_BASED_DIR64: u16 = 10; // // Platform-specific based relocation types. // pub const IMAGE_REL_BASED_IA64_IMM64: u16 = 9; pub const IMAGE_REL_BASED_MIPS_JMPADDR: u16 = 5; pub const IMAGE_REL_BASED_MIPS_JMPADDR16: u16 = 9; pub const IMAGE_REL_BASED_ARM_MOV32: u16 = 5; pub const IMAGE_REL_BASED_THUMB_MOV32: u16 = 7; pub const IMAGE_REL_BASED_RISCV_HIGH20: u16 = 5; pub const IMAGE_REL_BASED_RISCV_LOW12I: u16 = 7; pub const IMAGE_REL_BASED_RISCV_LOW12S: u16 = 8; // // Archive format. // pub const IMAGE_ARCHIVE_START_SIZE: usize = 8; pub const IMAGE_ARCHIVE_START: &[u8; 8] = b"!\n"; pub const IMAGE_ARCHIVE_END: &[u8] = b"`\n"; pub const IMAGE_ARCHIVE_PAD: &[u8] = b"\n"; pub const IMAGE_ARCHIVE_LINKER_MEMBER: &[u8; 16] = b"/ "; pub const IMAGE_ARCHIVE_LONGNAMES_MEMBER: &[u8; 16] = b"// "; pub const IMAGE_ARCHIVE_HYBRIDMAP_MEMBER: &[u8; 16] = b"// "; #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageArchiveMemberHeader { /// File member name - `/' terminated. pub name: [u8; 16], /// File member date - decimal. pub date: [u8; 12], /// File member user id - decimal. pub user_id: [u8; 6], /// File member group id - decimal. pub group_id: [u8; 6], /// File member mode - octal. pub mode: [u8; 8], /// File member size - decimal. pub size: [u8; 10], /// String to end header. pub end_header: [u8; 2], } pub const IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR: u16 = 60; // // DLL support. // // // Export Format // #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageExportDirectory { pub characteristics: U32, pub time_date_stamp: U32, pub major_version: U16, pub minor_version: U16, pub name: U32, pub base: U32, pub number_of_functions: U32, pub number_of_names: U32, /// RVA from base of image pub address_of_functions: U32, /// RVA from base of image pub address_of_names: U32, /// RVA from base of image pub address_of_name_ordinals: U32, } // // Import Format // #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageImportByName { pub hint: U16, //pub name: [i8; 1], } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageThunkData64(pub U64); /* union { /// PBYTE pub forwarder_string: U64, /// PDWORD pub function: U64, pub ordinal: U64, /// PIMAGE_IMPORT_BY_NAME pub address_of_data: U64, } u1; */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageThunkData32(pub U32); /* union { /// PBYTE pub forwarder_string: U32, /// PDWORD pub function: U32, pub ordinal: U32, /// PIMAGE_IMPORT_BY_NAME pub address_of_data: U32, } u1; } */ pub const IMAGE_ORDINAL_FLAG64: u64 = 0x8000000000000000; pub const IMAGE_ORDINAL_FLAG32: u32 = 0x80000000; /* #define IMAGE_ORDINAL64(Ordinal) (Ordinal & 0xffff) #define IMAGE_ORDINAL32(Ordinal) (Ordinal & 0xffff) #define IMAGE_SNAP_BY_ORDINAL64(Ordinal) ((Ordinal & IMAGE_ORDINAL_FLAG64) != 0) #define IMAGE_SNAP_BY_ORDINAL32(Ordinal) ((Ordinal & IMAGE_ORDINAL_FLAG32) != 0) */ // // Thread Local Storage // #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageTlsDirectory64 { pub start_address_of_raw_data: U64, pub end_address_of_raw_data: U64, /// PDWORD pub address_of_index: U64, /// PIMAGE_TLS_CALLBACK *; pub address_of_call_backs: U64, pub size_of_zero_fill: U32, pub characteristics: U32, } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageTlsDirectory32 { pub start_address_of_raw_data: U32, pub end_address_of_raw_data: U32, /// PDWORD pub address_of_index: U32, /// PIMAGE_TLS_CALLBACK * pub address_of_call_backs: U32, pub size_of_zero_fill: U32, pub characteristics: U32, } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageImportDescriptor { /// RVA to original unbound IAT (`ImageThunkData32`/`ImageThunkData64`) /// 0 for terminating null import descriptor pub original_first_thunk: U32Bytes, /// 0 if not bound, /// -1 if bound, and real date\time stamp /// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) /// O.W. date/time stamp of DLL bound to (Old BIND) pub time_date_stamp: U32Bytes, /// -1 if no forwarders pub forwarder_chain: U32Bytes, pub name: U32Bytes, /// RVA to IAT (if bound this IAT has actual addresses) pub first_thunk: U32Bytes, } impl ImageImportDescriptor { /// Tell whether this import descriptor is the null descriptor /// (used to mark the end of the iterator array in a PE) pub fn is_null(&self) -> bool { self.original_first_thunk.get(LE) == 0 && self.time_date_stamp.get(LE) == 0 && self.forwarder_chain.get(LE) == 0 && self.name.get(LE) == 0 && self.first_thunk.get(LE) == 0 } } // // New format import descriptors pointed to by DataDirectory[ IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT ] // #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageBoundImportDescriptor { pub time_date_stamp: U32, pub offset_module_name: U16, pub number_of_module_forwarder_refs: U16, // Array of zero or more IMAGE_BOUND_FORWARDER_REF follows } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageBoundForwarderRef { pub time_date_stamp: U32, pub offset_module_name: U16, pub reserved: U16, } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageDelayloadDescriptor { pub attributes: U32, /// RVA to the name of the target library (NULL-terminate ASCII string) pub dll_name_rva: U32, /// RVA to the HMODULE caching location (PHMODULE) pub module_handle_rva: U32, /// RVA to the start of the IAT (PIMAGE_THUNK_DATA) pub import_address_table_rva: U32, /// RVA to the start of the name table (PIMAGE_THUNK_DATA::AddressOfData) pub import_name_table_rva: U32, /// RVA to an optional bound IAT pub bound_import_address_table_rva: U32, /// RVA to an optional unload info table pub unload_information_table_rva: U32, /// 0 if not bound, otherwise, date/time of the target DLL pub time_date_stamp: U32, } impl ImageDelayloadDescriptor { /// Tell whether this delay-load import descriptor is the null descriptor /// (used to mark the end of the iterator array in a PE) pub fn is_null(&self) -> bool { self.attributes.get(LE) == 0 && self.dll_name_rva.get(LE) == 0 && self.module_handle_rva.get(LE) == 0 && self.import_address_table_rva.get(LE) == 0 && self.import_name_table_rva.get(LE) == 0 && self.bound_import_address_table_rva.get(LE) == 0 && self.unload_information_table_rva.get(LE) == 0 && self.time_date_stamp.get(LE) == 0 } } /// Delay load version 2 flag for `ImageDelayloadDescriptor::attributes`. pub const IMAGE_DELAYLOAD_RVA_BASED: u32 = 0x8000_0000; // // Resource Format. // // // Resource directory consists of two counts, following by a variable length // array of directory entries. The first count is the number of entries at // beginning of the array that have actual names associated with each entry. // The entries are in ascending order, case insensitive strings. The second // count is the number of entries that immediately follow the named entries. // This second count identifies the number of entries that have 16-bit integer // Ids as their name. These entries are also sorted in ascending order. // // This structure allows fast lookup by either name or number, but for any // given resource entry only one form of lookup is supported, not both. // This is consistent with the syntax of the .RC file and the .RES file. // #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageResourceDirectory { pub characteristics: U32, pub time_date_stamp: U32, pub major_version: U16, pub minor_version: U16, pub number_of_named_entries: U16, pub number_of_id_entries: U16, } pub const IMAGE_RESOURCE_NAME_IS_STRING: u32 = 0x8000_0000; pub const IMAGE_RESOURCE_DATA_IS_DIRECTORY: u32 = 0x8000_0000; // // Each directory contains the 32-bit Name of the entry and an offset, // relative to the beginning of the resource directory of the data associated // with this directory entry. If the name of the entry is an actual text // string instead of an integer Id, then the high order bit of the name field // is set to one and the low order 31-bits are an offset, relative to the // beginning of the resource directory of the string, which is of type // IMAGE_RESOURCE_DIRECTORY_STRING. Otherwise the high bit is clear and the // low-order 16-bits are the integer Id that identify this resource directory // entry. If the directory entry is yet another resource directory (i.e. a // subdirectory), then the high order bit of the offset field will be // set to indicate this. Otherwise the high bit is clear and the offset // field points to a resource data entry. // #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageResourceDirectoryEntry { pub name_or_id: U32, pub offset_to_data_or_directory: U32, } // // For resource directory entries that have actual string names, the Name // field of the directory entry points to an object of the following type. // All of these string objects are stored together after the last resource // directory entry and before the first resource data object. This minimizes // the impact of these variable length objects on the alignment of the fixed // size directory entry objects. // #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageResourceDirectoryString { pub length: U16, //pub name_string: [i8; 1], } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageResourceDirStringU { pub length: U16, //pub name_string: [U16; 1], } // // Each resource data entry describes a leaf node in the resource directory // tree. It contains an offset, relative to the beginning of the resource // directory of the data for the resource, a size field that gives the number // of bytes of data at that offset, a CodePage that should be used when // decoding code point values within the resource data. Typically for new // applications the code page would be the unicode code page. // #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageResourceDataEntry { /// RVA of the data. pub offset_to_data: U32, pub size: U32, pub code_page: U32, pub reserved: U32, } // Resource type: https://docs.microsoft.com/en-us/windows/win32/menurc/resource-types /// ID for: Hardware-dependent cursor resource. pub const RT_CURSOR: u16 = 1; /// ID for: Bitmap resource. pub const RT_BITMAP: u16 = 2; /// ID for: Hardware-dependent icon resource. pub const RT_ICON: u16 = 3; /// ID for: Menu resource. pub const RT_MENU: u16 = 4; /// ID for: Dialog box. pub const RT_DIALOG: u16 = 5; /// ID for: String-table entry. pub const RT_STRING: u16 = 6; /// ID for: Font directory resource. pub const RT_FONTDIR: u16 = 7; /// ID for: Font resource. pub const RT_FONT: u16 = 8; /// ID for: Accelerator table. pub const RT_ACCELERATOR: u16 = 9; /// ID for: Application-defined resource (raw data). pub const RT_RCDATA: u16 = 10; /// ID for: Message-table entry. pub const RT_MESSAGETABLE: u16 = 11; /// ID for: Hardware-independent cursor resource. pub const RT_GROUP_CURSOR: u16 = 12; /// ID for: Hardware-independent icon resource. pub const RT_GROUP_ICON: u16 = 14; /// ID for: Version resource. pub const RT_VERSION: u16 = 16; /// ID for: Allows a resource editing tool to associate a string with an .rc file. pub const RT_DLGINCLUDE: u16 = 17; /// ID for: Plug and Play resource. pub const RT_PLUGPLAY: u16 = 19; /// ID for: VXD. pub const RT_VXD: u16 = 20; /// ID for: Animated cursor. pub const RT_ANICURSOR: u16 = 21; /// ID for: Animated icon. pub const RT_ANIICON: u16 = 22; /// ID for: HTML resource. pub const RT_HTML: u16 = 23; /// ID for: Side-by-Side Assembly Manifest. pub const RT_MANIFEST: u16 = 24; // // Code Integrity in loadconfig (CI) // #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageLoadConfigCodeIntegrity { /// Flags to indicate if CI information is available, etc. pub flags: U16, /// 0xFFFF means not available pub catalog: U16, pub catalog_offset: U32, /// Additional bitmask to be defined later pub reserved: U32, } // // Dynamic value relocation table in loadconfig // #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageDynamicRelocationTable { pub version: U32, pub size: U32, // DynamicRelocations: [ImageDynamicRelocation; 0], } // // Dynamic value relocation entries following IMAGE_DYNAMIC_RELOCATION_TABLE // #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageDynamicRelocation32 { pub symbol: U32, pub base_reloc_size: U32, // BaseRelocations: [ImageBaseRelocation; 0], } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageDynamicRelocation64 { pub symbol: U64, pub base_reloc_size: U32, // BaseRelocations: [ImageBaseRelocation; 0], } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageDynamicRelocation32V2 { pub header_size: U32, pub fixup_info_size: U32, pub symbol: U32, pub symbol_group: U32, pub flags: U32, // ... variable length header fields // pub fixup_info: [u8; fixup_info_size] } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageDynamicRelocation64V2 { pub header_size: U32, pub fixup_info_size: U32, pub symbol: U64, pub symbol_group: U32, pub flags: U32, // ... variable length header fields // pub fixup_info[u8; fixup_info_size] } // // Defined symbolic dynamic relocation entries. // pub const IMAGE_DYNAMIC_RELOCATION_GUARD_RF_PROLOGUE: u32 = 0x0000_0001; pub const IMAGE_DYNAMIC_RELOCATION_GUARD_RF_EPILOGUE: u32 = 0x0000_0002; pub const IMAGE_DYNAMIC_RELOCATION_GUARD_IMPORT_CONTROL_TRANSFER: u32 = 0x0000_0003; pub const IMAGE_DYNAMIC_RELOCATION_GUARD_INDIR_CONTROL_TRANSFER: u32 = 0x0000_0004; pub const IMAGE_DYNAMIC_RELOCATION_GUARD_SWITCHTABLE_BRANCH: u32 = 0x0000_0005; // This struct has alignment 1. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImagePrologueDynamicRelocationHeader { pub prologue_byte_count: u8, // pub prologue_bytes: [u8; prologue_byte_count], } // This struct has alignment 1. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageEpilogueDynamicRelocationHeader { pub epilogue_count: U32Bytes, pub epilogue_byte_count: u8, pub branch_descriptor_element_size: u8, pub branch_descriptor_count: U16Bytes, // pub branch_descriptors[...], // pub branch_descriptor_bit_map[...], } /* // TODO? bitfields // TODO: unaligned? #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageImportControlTransferDynamicRelocation { DWORD PageRelativeOffset : 12; DWORD IndirectCall : 1; DWORD IATIndex : 19; } // TODO: unaligned? #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageIndirControlTransferDynamicRelocation { WORD PageRelativeOffset : 12; WORD IndirectCall : 1; WORD RexWPrefix : 1; WORD CfgCheck : 1; WORD Reserved : 1; } // TODO: unaligned? #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageSwitchtableBranchDynamicRelocation { WORD PageRelativeOffset : 12; WORD RegisterNumber : 4; } */ // // Load Configuration Directory Entry // #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageLoadConfigDirectory32 { pub size: U32, pub time_date_stamp: U32, pub major_version: U16, pub minor_version: U16, pub global_flags_clear: U32, pub global_flags_set: U32, pub critical_section_default_timeout: U32, pub de_commit_free_block_threshold: U32, pub de_commit_total_free_threshold: U32, /// VA pub lock_prefix_table: U32, pub maximum_allocation_size: U32, pub virtual_memory_threshold: U32, pub process_heap_flags: U32, pub process_affinity_mask: U32, pub csd_version: U16, pub dependent_load_flags: U16, /// VA pub edit_list: U32, /// VA pub security_cookie: U32, /// VA pub sehandler_table: U32, pub sehandler_count: U32, /// VA pub guard_cf_check_function_pointer: U32, /// VA pub guard_cf_dispatch_function_pointer: U32, /// VA pub guard_cf_function_table: U32, pub guard_cf_function_count: U32, pub guard_flags: U32, pub code_integrity: ImageLoadConfigCodeIntegrity, /// VA pub guard_address_taken_iat_entry_table: U32, pub guard_address_taken_iat_entry_count: U32, /// VA pub guard_long_jump_target_table: U32, pub guard_long_jump_target_count: U32, /// VA pub dynamic_value_reloc_table: U32, pub chpe_metadata_pointer: U32, /// VA pub guard_rf_failure_routine: U32, /// VA pub guard_rf_failure_routine_function_pointer: U32, pub dynamic_value_reloc_table_offset: U32, pub dynamic_value_reloc_table_section: U16, pub reserved2: U16, /// VA pub guard_rf_verify_stack_pointer_function_pointer: U32, pub hot_patch_table_offset: U32, pub reserved3: U32, /// VA pub enclave_configuration_pointer: U32, /// VA pub volatile_metadata_pointer: U32, } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageLoadConfigDirectory64 { pub size: U32, pub time_date_stamp: U32, pub major_version: U16, pub minor_version: U16, pub global_flags_clear: U32, pub global_flags_set: U32, pub critical_section_default_timeout: U32, pub de_commit_free_block_threshold: U64, pub de_commit_total_free_threshold: U64, /// VA pub lock_prefix_table: U64, pub maximum_allocation_size: U64, pub virtual_memory_threshold: U64, pub process_affinity_mask: U64, pub process_heap_flags: U32, pub csd_version: U16, pub dependent_load_flags: U16, /// VA pub edit_list: U64, /// VA pub security_cookie: U64, /// VA pub sehandler_table: U64, pub sehandler_count: U64, /// VA pub guard_cf_check_function_pointer: U64, /// VA pub guard_cf_dispatch_function_pointer: U64, /// VA pub guard_cf_function_table: U64, pub guard_cf_function_count: U64, pub guard_flags: U32, pub code_integrity: ImageLoadConfigCodeIntegrity, /// VA pub guard_address_taken_iat_entry_table: U64, pub guard_address_taken_iat_entry_count: U64, /// VA pub guard_long_jump_target_table: U64, pub guard_long_jump_target_count: U64, /// VA pub dynamic_value_reloc_table: U64, /// VA pub chpe_metadata_pointer: U64, /// VA pub guard_rf_failure_routine: U64, /// VA pub guard_rf_failure_routine_function_pointer: U64, pub dynamic_value_reloc_table_offset: U32, pub dynamic_value_reloc_table_section: U16, pub reserved2: U16, /// VA pub guard_rf_verify_stack_pointer_function_pointer: U64, pub hot_patch_table_offset: U32, pub reserved3: U32, /// VA pub enclave_configuration_pointer: U64, /// VA pub volatile_metadata_pointer: U64, } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageHotPatchInfo { pub version: U32, pub size: U32, pub sequence_number: U32, pub base_image_list: U32, pub base_image_count: U32, /// Version 2 and later pub buffer_offset: U32, /// Version 3 and later pub extra_patch_size: U32, } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageHotPatchBase { pub sequence_number: U32, pub flags: U32, pub original_time_date_stamp: U32, pub original_check_sum: U32, pub code_integrity_info: U32, pub code_integrity_size: U32, pub patch_table: U32, /// Version 2 and later pub buffer_offset: U32, } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageHotPatchHashes { pub sha256: [u8; 32], pub sha1: [u8; 20], } pub const IMAGE_HOT_PATCH_BASE_OBLIGATORY: u32 = 0x0000_0001; pub const IMAGE_HOT_PATCH_BASE_CAN_ROLL_BACK: u32 = 0x0000_0002; pub const IMAGE_HOT_PATCH_CHUNK_INVERSE: u32 = 0x8000_0000; pub const IMAGE_HOT_PATCH_CHUNK_OBLIGATORY: u32 = 0x4000_0000; pub const IMAGE_HOT_PATCH_CHUNK_RESERVED: u32 = 0x3FF0_3000; pub const IMAGE_HOT_PATCH_CHUNK_TYPE: u32 = 0x000F_C000; pub const IMAGE_HOT_PATCH_CHUNK_SOURCE_RVA: u32 = 0x0000_8000; pub const IMAGE_HOT_PATCH_CHUNK_TARGET_RVA: u32 = 0x0000_4000; pub const IMAGE_HOT_PATCH_CHUNK_SIZE: u32 = 0x0000_0FFF; pub const IMAGE_HOT_PATCH_NONE: u32 = 0x0000_0000; pub const IMAGE_HOT_PATCH_FUNCTION: u32 = 0x0001_C000; pub const IMAGE_HOT_PATCH_ABSOLUTE: u32 = 0x0002_C000; pub const IMAGE_HOT_PATCH_REL32: u32 = 0x0003_C000; pub const IMAGE_HOT_PATCH_CALL_TARGET: u32 = 0x0004_4000; pub const IMAGE_HOT_PATCH_INDIRECT: u32 = 0x0005_C000; pub const IMAGE_HOT_PATCH_NO_CALL_TARGET: u32 = 0x0006_4000; pub const IMAGE_HOT_PATCH_DYNAMIC_VALUE: u32 = 0x0007_8000; /// Module performs control flow integrity checks using system-supplied support pub const IMAGE_GUARD_CF_INSTRUMENTED: u32 = 0x0000_0100; /// Module performs control flow and write integrity checks pub const IMAGE_GUARD_CFW_INSTRUMENTED: u32 = 0x0000_0200; /// Module contains valid control flow target metadata pub const IMAGE_GUARD_CF_FUNCTION_TABLE_PRESENT: u32 = 0x0000_0400; /// Module does not make use of the /GS security cookie pub const IMAGE_GUARD_SECURITY_COOKIE_UNUSED: u32 = 0x0000_0800; /// Module supports read only delay load IAT pub const IMAGE_GUARD_PROTECT_DELAYLOAD_IAT: u32 = 0x0000_1000; /// Delayload import table in its own .didat section (with nothing else in it) that can be freely reprotected pub const IMAGE_GUARD_DELAYLOAD_IAT_IN_ITS_OWN_SECTION: u32 = 0x0000_2000; /// Module contains suppressed export information. /// /// This also infers that the address taken taken IAT table is also present in the load config. pub const IMAGE_GUARD_CF_EXPORT_SUPPRESSION_INFO_PRESENT: u32 = 0x0000_4000; /// Module enables suppression of exports pub const IMAGE_GUARD_CF_ENABLE_EXPORT_SUPPRESSION: u32 = 0x0000_8000; /// Module contains longjmp target information pub const IMAGE_GUARD_CF_LONGJUMP_TABLE_PRESENT: u32 = 0x0001_0000; /// Module contains return flow instrumentation and metadata pub const IMAGE_GUARD_RF_INSTRUMENTED: u32 = 0x0002_0000; /// Module requests that the OS enable return flow protection pub const IMAGE_GUARD_RF_ENABLE: u32 = 0x0004_0000; /// Module requests that the OS enable return flow protection in strict mode pub const IMAGE_GUARD_RF_STRICT: u32 = 0x0008_0000; /// Module was built with retpoline support pub const IMAGE_GUARD_RETPOLINE_PRESENT: u32 = 0x0010_0000; /// Stride of Guard CF function table encoded in these bits (additional count of bytes per element) pub const IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK: u32 = 0xF000_0000; /// Shift to right-justify Guard CF function table stride pub const IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT: u32 = 28; // // GFIDS table entry flags. // /// The containing GFID entry is suppressed pub const IMAGE_GUARD_FLAG_FID_SUPPRESSED: u16 = 0x01; /// The containing GFID entry is export suppressed pub const IMAGE_GUARD_FLAG_EXPORT_SUPPRESSED: u16 = 0x02; // // WIN CE Exception table format // // // Function table entry format. Function table is pointed to by the // IMAGE_DIRECTORY_ENTRY_EXCEPTION directory entry. // /* // TODO? bitfields #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageCeRuntimeFunctionEntry { pub func_start: U32, DWORD PrologLen : 8; DWORD FuncLen : 22; DWORD ThirtyTwoBit : 1; DWORD ExceptionFlag : 1; } */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageArmRuntimeFunctionEntry { pub begin_address: U32, pub unwind_data: U32, } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageArm64RuntimeFunctionEntry { pub begin_address: U32, pub unwind_data: U32, } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageAlpha64RuntimeFunctionEntry { pub begin_address: U64, pub end_address: U64, pub exception_handler: U64, pub handler_data: U64, pub prolog_end_address: U64, } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageAlphaRuntimeFunctionEntry { pub begin_address: U32, pub end_address: U32, pub exception_handler: U32, pub handler_data: U32, pub prolog_end_address: U32, } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageRuntimeFunctionEntry { pub begin_address: U32, pub end_address: U32, pub unwind_info_address_or_data: U32, } // // Software enclave information // pub const IMAGE_ENCLAVE_LONG_ID_LENGTH: usize = 32; pub const IMAGE_ENCLAVE_SHORT_ID_LENGTH: usize = 16; #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageEnclaveConfig32 { pub size: U32, pub minimum_required_config_size: U32, pub policy_flags: U32, pub number_of_imports: U32, pub import_list: U32, pub import_entry_size: U32, pub family_id: [u8; IMAGE_ENCLAVE_SHORT_ID_LENGTH], pub image_id: [u8; IMAGE_ENCLAVE_SHORT_ID_LENGTH], pub image_version: U32, pub security_version: U32, pub enclave_size: U32, pub number_of_threads: U32, pub enclave_flags: U32, } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageEnclaveConfig64 { pub size: U32, pub minimum_required_config_size: U32, pub policy_flags: U32, pub number_of_imports: U32, pub import_list: U32, pub import_entry_size: U32, pub family_id: [u8; IMAGE_ENCLAVE_SHORT_ID_LENGTH], pub image_id: [u8; IMAGE_ENCLAVE_SHORT_ID_LENGTH], pub image_version: U32, pub security_version: U32, pub enclave_size: U64, pub number_of_threads: U32, pub enclave_flags: U32, } //pub const IMAGE_ENCLAVE_MINIMUM_CONFIG_SIZE: usize = FIELD_OFFSET(IMAGE_ENCLAVE_CONFIG, EnclaveFlags); pub const IMAGE_ENCLAVE_POLICY_DEBUGGABLE: u32 = 0x0000_0001; pub const IMAGE_ENCLAVE_FLAG_PRIMARY_IMAGE: u32 = 0x0000_0001; #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageEnclaveImport { pub match_type: U32, pub minimum_security_version: U32, pub unique_or_author_id: [u8; IMAGE_ENCLAVE_LONG_ID_LENGTH], pub family_id: [u8; IMAGE_ENCLAVE_SHORT_ID_LENGTH], pub image_id: [u8; IMAGE_ENCLAVE_SHORT_ID_LENGTH], pub import_name: U32, pub reserved: U32, } pub const IMAGE_ENCLAVE_IMPORT_MATCH_NONE: u32 = 0x0000_0000; pub const IMAGE_ENCLAVE_IMPORT_MATCH_UNIQUE_ID: u32 = 0x0000_0001; pub const IMAGE_ENCLAVE_IMPORT_MATCH_AUTHOR_ID: u32 = 0x0000_0002; pub const IMAGE_ENCLAVE_IMPORT_MATCH_FAMILY_ID: u32 = 0x0000_0003; pub const IMAGE_ENCLAVE_IMPORT_MATCH_IMAGE_ID: u32 = 0x0000_0004; // // Debug Format // #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageDebugDirectory { pub characteristics: U32, pub time_date_stamp: U32, pub major_version: U16, pub minor_version: U16, pub typ: U32, pub size_of_data: U32, pub address_of_raw_data: U32, pub pointer_to_raw_data: U32, } pub const IMAGE_DEBUG_TYPE_UNKNOWN: u32 = 0; pub const IMAGE_DEBUG_TYPE_COFF: u32 = 1; pub const IMAGE_DEBUG_TYPE_CODEVIEW: u32 = 2; pub const IMAGE_DEBUG_TYPE_FPO: u32 = 3; pub const IMAGE_DEBUG_TYPE_MISC: u32 = 4; pub const IMAGE_DEBUG_TYPE_EXCEPTION: u32 = 5; pub const IMAGE_DEBUG_TYPE_FIXUP: u32 = 6; pub const IMAGE_DEBUG_TYPE_OMAP_TO_SRC: u32 = 7; pub const IMAGE_DEBUG_TYPE_OMAP_FROM_SRC: u32 = 8; pub const IMAGE_DEBUG_TYPE_BORLAND: u32 = 9; pub const IMAGE_DEBUG_TYPE_RESERVED10: u32 = 10; pub const IMAGE_DEBUG_TYPE_CLSID: u32 = 11; pub const IMAGE_DEBUG_TYPE_VC_FEATURE: u32 = 12; pub const IMAGE_DEBUG_TYPE_POGO: u32 = 13; pub const IMAGE_DEBUG_TYPE_ILTCG: u32 = 14; pub const IMAGE_DEBUG_TYPE_MPX: u32 = 15; pub const IMAGE_DEBUG_TYPE_REPRO: u32 = 16; #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageCoffSymbolsHeader { pub number_of_symbols: U32, pub lva_to_first_symbol: U32, pub number_of_linenumbers: U32, pub lva_to_first_linenumber: U32, pub rva_to_first_byte_of_code: U32, pub rva_to_last_byte_of_code: U32, pub rva_to_first_byte_of_data: U32, pub rva_to_last_byte_of_data: U32, } pub const FRAME_FPO: u16 = 0; pub const FRAME_TRAP: u16 = 1; pub const FRAME_TSS: u16 = 2; pub const FRAME_NONFPO: u16 = 3; /* // TODO? bitfields #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct FpoData { /// offset 1st byte of function code pub ul_off_start: U32, /// # bytes in function pub cb_proc_size: U32, /// # bytes in locals/4 pub cdw_locals: U32, /// # bytes in params/4 pub cdw_params: U16, /// # bytes in prolog WORD cbProlog : 8; /// # regs saved WORD cbRegs : 3; /// TRUE if SEH in func WORD fHasSEH : 1; /// TRUE if EBP has been allocated WORD fUseBP : 1; /// reserved for future use WORD reserved : 1; /// frame type WORD cbFrame : 2; } pub const SIZEOF_RFPO_DATA: usize = 16; */ pub const IMAGE_DEBUG_MISC_EXENAME: u16 = 1; #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageDebugMisc { /// type of misc data, see defines pub data_type: U32, /// total length of record, rounded to four byte multiple. pub length: U32, /// TRUE if data is unicode string pub unicode: u8, pub reserved: [u8; 3], // Actual data //pub data: [u8; 1], } // // Function table extracted from MIPS/ALPHA/IA64 images. Does not contain // information needed only for runtime support. Just those fields for // each entry needed by a debugger. // #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageFunctionEntry { pub starting_address: U32, pub ending_address: U32, pub end_of_prologue: U32, } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageFunctionEntry64 { pub starting_address: U64, pub ending_address: U64, pub end_of_prologue_or_unwind_info_address: U64, } // // Debugging information can be stripped from an image file and placed // in a separate .DBG file, whose file name part is the same as the // image file name part (e.g. symbols for CMD.EXE could be stripped // and placed in CMD.DBG). This is indicated by the IMAGE_FILE_DEBUG_STRIPPED // flag in the Characteristics field of the file header. The beginning of // the .DBG file contains the following structure which captures certain // information from the image file. This allows a debug to proceed even if // the original image file is not accessible. This header is followed by // zero of more IMAGE_SECTION_HEADER structures, followed by zero or more // IMAGE_DEBUG_DIRECTORY structures. The latter structures and those in // the image file contain file offsets relative to the beginning of the // .DBG file. // // If symbols have been stripped from an image, the IMAGE_DEBUG_MISC structure // is left in the image file, but not mapped. This allows a debugger to // compute the name of the .DBG file, from the name of the image in the // IMAGE_DEBUG_MISC structure. // #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageSeparateDebugHeader { pub signature: U16, pub flags: U16, pub machine: U16, pub characteristics: U16, pub time_date_stamp: U32, pub check_sum: U32, pub image_base: U32, pub size_of_image: U32, pub number_of_sections: U32, pub exported_names_size: U32, pub debug_directory_size: U32, pub section_alignment: U32, pub reserved: [U32; 2], } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct NonPagedDebugInfo { pub signature: U16, pub flags: U16, pub size: U32, pub machine: U16, pub characteristics: U16, pub time_date_stamp: U32, pub check_sum: U32, pub size_of_image: U32, pub image_base: U64, //debug_directory_size //ImageDebugDirectory } pub const IMAGE_SEPARATE_DEBUG_SIGNATURE: u16 = 0x4944; pub const NON_PAGED_DEBUG_SIGNATURE: u16 = 0x494E; pub const IMAGE_SEPARATE_DEBUG_FLAGS_MASK: u16 = 0x8000; /// when DBG was updated, the old checksum didn't match. pub const IMAGE_SEPARATE_DEBUG_MISMATCH: u16 = 0x8000; // // The .arch section is made up of headers, each describing an amask position/value // pointing to an array of IMAGE_ARCHITECTURE_ENTRY's. Each "array" (both the header // and entry arrays) are terminiated by a quadword of 0xffffffffL. // // NOTE: There may be quadwords of 0 sprinkled around and must be skipped. // /* // TODO? bitfields #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageArchitectureHeader { /// 1 -> code section depends on mask bit /// 0 -> new instruction depends on mask bit unsigned int AmaskValue: 1; /// MBZ int :7; /// Amask bit in question for this fixup unsigned int AmaskShift: 8; /// MBZ int :16; /// RVA into .arch section to array of ARCHITECTURE_ENTRY's pub first_entry_rva: U32, } */ #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageArchitectureEntry { /// RVA of instruction to fixup pub fixup_inst_rva: U32, /// fixup instruction (see alphaops.h) pub new_inst: U32, } // The following structure defines the new import object. Note the values of the first two fields, // which must be set as stated in order to differentiate old and new import members. // Following this structure, the linker emits two null-terminated strings used to recreate the // import at the time of use. The first string is the import's name, the second is the dll's name. pub const IMPORT_OBJECT_HDR_SIG2: u16 = 0xffff; #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImportObjectHeader { /// Must be IMAGE_FILE_MACHINE_UNKNOWN pub sig1: U16, /// Must be IMPORT_OBJECT_HDR_SIG2. pub sig2: U16, pub version: U16, pub machine: U16, /// Time/date stamp pub time_date_stamp: U32, /// particularly useful for incremental links pub size_of_data: U32, /// if grf & IMPORT_OBJECT_ORDINAL pub ordinal_or_hint: U16, // WORD Type : 2; // WORD NameType : 3; // WORD Reserved : 11; pub name_type: U16, } pub const IMPORT_OBJECT_TYPE_MASK: u16 = 0b11; pub const IMPORT_OBJECT_TYPE_SHIFT: u16 = 0; pub const IMPORT_OBJECT_CODE: u16 = 0; pub const IMPORT_OBJECT_DATA: u16 = 1; pub const IMPORT_OBJECT_CONST: u16 = 2; pub const IMPORT_OBJECT_NAME_MASK: u16 = 0b111; pub const IMPORT_OBJECT_NAME_SHIFT: u16 = 2; /// Import by ordinal pub const IMPORT_OBJECT_ORDINAL: u16 = 0; /// Import name == public symbol name. pub const IMPORT_OBJECT_NAME: u16 = 1; /// Import name == public symbol name skipping leading ?, @, or optionally _. pub const IMPORT_OBJECT_NAME_NO_PREFIX: u16 = 2; /// Import name == public symbol name skipping leading ?, @, or optionally _ and truncating at first @. pub const IMPORT_OBJECT_NAME_UNDECORATE: u16 = 3; /// Import name == a name is explicitly provided after the DLL name. pub const IMPORT_OBJECT_NAME_EXPORTAS: u16 = 4; // COM+ Header entry point flags. pub const COMIMAGE_FLAGS_ILONLY: u32 = 0x0000_0001; pub const COMIMAGE_FLAGS_32BITREQUIRED: u32 = 0x0000_0002; pub const COMIMAGE_FLAGS_IL_LIBRARY: u32 = 0x0000_0004; pub const COMIMAGE_FLAGS_STRONGNAMESIGNED: u32 = 0x0000_0008; pub const COMIMAGE_FLAGS_NATIVE_ENTRYPOINT: u32 = 0x0000_0010; pub const COMIMAGE_FLAGS_TRACKDEBUGDATA: u32 = 0x0001_0000; pub const COMIMAGE_FLAGS_32BITPREFERRED: u32 = 0x0002_0000; // Version flags for image. pub const COR_VERSION_MAJOR_V2: u16 = 2; pub const COR_VERSION_MAJOR: u16 = COR_VERSION_MAJOR_V2; pub const COR_VERSION_MINOR: u16 = 5; pub const COR_DELETED_NAME_LENGTH: usize = 8; pub const COR_VTABLEGAP_NAME_LENGTH: usize = 8; // Maximum size of a NativeType descriptor. pub const NATIVE_TYPE_MAX_CB: u16 = 1; pub const COR_ILMETHOD_SECT_SMALL_MAX_DATASIZE: u16 = 0xFF; // Consts for the MIH FLAGS pub const IMAGE_COR_MIH_METHODRVA: u16 = 0x01; pub const IMAGE_COR_MIH_EHRVA: u16 = 0x02; pub const IMAGE_COR_MIH_BASICBLOCK: u16 = 0x08; // V-table constants /// V-table slots are 32-bits in size. pub const COR_VTABLE_32BIT: u16 = 0x01; /// V-table slots are 64-bits in size. pub const COR_VTABLE_64BIT: u16 = 0x02; /// If set, transition from unmanaged. pub const COR_VTABLE_FROM_UNMANAGED: u16 = 0x04; /// If set, transition from unmanaged with keeping the current appdomain. pub const COR_VTABLE_FROM_UNMANAGED_RETAIN_APPDOMAIN: u16 = 0x08; /// Call most derived method described by pub const COR_VTABLE_CALL_MOST_DERIVED: u16 = 0x10; // EATJ constants /// Size of a jump thunk reserved range. pub const IMAGE_COR_EATJ_THUNK_SIZE: usize = 32; // Max name lengths pub const MAX_CLASS_NAME: usize = 1024; pub const MAX_PACKAGE_NAME: usize = 1024; // CLR 2.0 header structure. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ImageCor20Header { // Header versioning pub cb: U32, pub major_runtime_version: U16, pub minor_runtime_version: U16, // Symbol table and startup information pub meta_data: ImageDataDirectory, pub flags: U32, // If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is not set, EntryPointToken represents a managed entrypoint. // If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is set, EntryPointRVA represents an RVA to a native entrypoint. pub entry_point_token_or_rva: U32, // Binding information pub resources: ImageDataDirectory, pub strong_name_signature: ImageDataDirectory, // Regular fixup and binding information pub code_manager_table: ImageDataDirectory, pub vtable_fixups: ImageDataDirectory, pub export_address_table_jumps: ImageDataDirectory, // Precompiled image info (internal use only - set to zero) pub managed_native_header: ImageDataDirectory, } unsafe_impl_pod!( ImageDosHeader, ImageOs2Header, ImageVxdHeader, ImageFileHeader, ImageDataDirectory, ImageOptionalHeader32, ImageRomOptionalHeader, ImageOptionalHeader64, ImageNtHeaders64, ImageNtHeaders32, ImageRomHeaders, Guid, AnonObjectHeader, AnonObjectHeaderV2, AnonObjectHeaderBigobj, ImageSectionHeader, ImageSymbol, ImageSymbolBytes, ImageSymbolEx, ImageSymbolExBytes, ImageAuxSymbolTokenDef, ImageAuxSymbolFunction, ImageAuxSymbolFunctionBeginEnd, ImageAuxSymbolWeak, ImageAuxSymbolSection, ImageAuxSymbolCrc, ImageRelocation, ImageLinenumber, ImageBaseRelocation, ImageArchiveMemberHeader, ImageExportDirectory, ImageImportByName, ImageThunkData64, ImageThunkData32, ImageTlsDirectory64, ImageTlsDirectory32, ImageImportDescriptor, ImageBoundImportDescriptor, ImageBoundForwarderRef, ImageDelayloadDescriptor, ImageResourceDirectory, ImageResourceDirectoryEntry, ImageResourceDirectoryString, ImageResourceDirStringU, ImageResourceDataEntry, ImageLoadConfigCodeIntegrity, ImageDynamicRelocationTable, ImageDynamicRelocation32, ImageDynamicRelocation64, ImageDynamicRelocation32V2, ImageDynamicRelocation64V2, ImagePrologueDynamicRelocationHeader, ImageEpilogueDynamicRelocationHeader, //ImageImportControlTransferDynamicRelocation, //ImageIndirControlTransferDynamicRelocation, //ImageSwitchtableBranchDynamicRelocation, ImageLoadConfigDirectory32, ImageLoadConfigDirectory64, ImageHotPatchInfo, ImageHotPatchBase, ImageHotPatchHashes, //ImageCeRuntimeFunctionEntry, ImageArmRuntimeFunctionEntry, ImageArm64RuntimeFunctionEntry, ImageAlpha64RuntimeFunctionEntry, ImageAlphaRuntimeFunctionEntry, ImageRuntimeFunctionEntry, ImageEnclaveConfig32, ImageEnclaveConfig64, ImageEnclaveImport, ImageDebugDirectory, ImageCoffSymbolsHeader, //FpoData, ImageDebugMisc, ImageFunctionEntry, ImageFunctionEntry64, ImageSeparateDebugHeader, NonPagedDebugInfo, //ImageArchitectureHeader, ImageArchitectureEntry, ImportObjectHeader, ImageCor20Header, MaskedRichHeaderEntry, ); object-0.36.5/src/pod.rs000064400000000000000000000225451046102023000131140ustar 00000000000000//! Tools for converting file format structures to and from bytes. //! //! This module should be replaced once rust provides safe transmutes. // This module provides functions for both read and write features. #![cfg_attr( not(all(feature = "read_core", feature = "write_core")), allow(dead_code) )] use core::{mem, result, slice}; type Result = result::Result; /// A trait for types that can safely be converted from and to byte slices. /// /// # Safety /// A type that is `Pod` must: /// - be `#[repr(C)]` or `#[repr(transparent)]` /// - have no invalid byte values /// - have no padding pub unsafe trait Pod: Copy + 'static {} /// Cast the head of a byte slice to a `Pod` type. /// /// Returns the type and the tail of the byte slice. /// /// Returns an error if the byte slice is too short or the alignment is invalid. #[inline] pub fn from_bytes(data: &[u8]) -> Result<(&T, &[u8])> { let size = mem::size_of::(); let tail = data.get(size..).ok_or(())?; let ptr = data.as_ptr(); if (ptr as usize) % mem::align_of::() != 0 { return Err(()); } // Safety: // The alignment and size are checked by this function. // The Pod trait ensures the type is valid to cast from bytes. let val = unsafe { &*ptr.cast() }; Ok((val, tail)) } /// Cast the head of a mutable byte slice to a `Pod` type. /// /// Returns the type and the tail of the byte slice. /// /// Returns an error if the byte slice is too short or the alignment is invalid. #[inline] pub fn from_bytes_mut(data: &mut [u8]) -> Result<(&mut T, &mut [u8])> { let size = mem::size_of::(); if size > data.len() { return Err(()); } let (data, tail) = data.split_at_mut(size); let ptr = data.as_mut_ptr(); if (ptr as usize) % mem::align_of::() != 0 { return Err(()); } // Safety: // The alignment and size are checked by this function. // The Pod trait ensures the type is valid to cast from bytes. let val = unsafe { &mut *ptr.cast() }; Ok((val, tail)) } /// Cast the head of a byte slice to a slice of a `Pod` type. /// /// Returns the type slice and the tail of the byte slice. /// /// Returns an error if the byte slice is too short or the alignment is invalid. #[inline] pub fn slice_from_bytes(data: &[u8], count: usize) -> Result<(&[T], &[u8])> { let size = count.checked_mul(mem::size_of::()).ok_or(())?; let tail = data.get(size..).ok_or(())?; let ptr = data.as_ptr(); if (ptr as usize) % mem::align_of::() != 0 { return Err(()); } // Safety: // The alignment and size are checked by this function. // The Pod trait ensures the type is valid to cast from bytes. let slice = unsafe { slice::from_raw_parts(ptr.cast(), count) }; Ok((slice, tail)) } /// Cast the head of a mutable byte slice to a slice of a `Pod` type. /// /// Returns the type slice and the tail of the byte slice. /// /// Returns an error if the byte slice is too short or the alignment is invalid. #[inline] pub fn slice_from_bytes_mut( data: &mut [u8], count: usize, ) -> Result<(&mut [T], &mut [u8])> { let size = count.checked_mul(mem::size_of::()).ok_or(())?; if size > data.len() { return Err(()); } let (data, tail) = data.split_at_mut(size); let ptr = data.as_mut_ptr(); if (ptr as usize) % mem::align_of::() != 0 { return Err(()); } // Safety: // The alignment and size are checked by this function. // The Pod trait ensures the type is valid to cast from bytes. let slice = unsafe { slice::from_raw_parts_mut(ptr.cast(), count) }; Ok((slice, tail)) } /// Cast all of a byte slice to a slice of a `Pod` type. /// /// Returns the type slice. /// /// Returns an error if the size of the byte slice is not an exact multiple /// of the type size, or the alignment is invalid. #[inline] pub fn slice_from_all_bytes(data: &[u8]) -> Result<&[T]> { let count = data.len() / mem::size_of::(); let (slice, tail) = slice_from_bytes(data, count)?; if !tail.is_empty() { return Err(()); } Ok(slice) } /// Cast all of a byte slice to a slice of a `Pod` type. /// /// Returns the type slice. /// /// Returns an error if the size of the byte slice is not an exact multiple /// of the type size, or the alignment is invalid. #[inline] pub fn slice_from_all_bytes_mut(data: &mut [u8]) -> Result<&mut [T]> { let count = data.len() / mem::size_of::(); let (slice, tail) = slice_from_bytes_mut(data, count)?; if !tail.is_empty() { return Err(()); } Ok(slice) } /// Cast a `Pod` type to a byte slice. #[inline] pub fn bytes_of(val: &T) -> &[u8] { let size = mem::size_of::(); // Safety: // Any alignment is allowed. // The size is determined in this function. // The Pod trait ensures the type is valid to cast to bytes. unsafe { slice::from_raw_parts(slice::from_ref(val).as_ptr().cast(), size) } } /// Cast a `Pod` type to a mutable byte slice. #[inline] pub fn bytes_of_mut(val: &mut T) -> &mut [u8] { let size = mem::size_of::(); // Safety: // Any alignment is allowed. // The size is determined in this function. // The Pod trait ensures the type is valid to cast to bytes. unsafe { slice::from_raw_parts_mut(slice::from_mut(val).as_mut_ptr().cast(), size) } } /// Cast a slice of a `Pod` type to a byte slice. #[inline] pub fn bytes_of_slice(val: &[T]) -> &[u8] { let size = val.len().wrapping_mul(mem::size_of::()); // Safety: // Any alignment is allowed. // The size is determined in this function. // The Pod trait ensures the type is valid to cast to bytes. unsafe { slice::from_raw_parts(val.as_ptr().cast(), size) } } /// Cast a slice of a `Pod` type to a mutable byte slice. #[inline] pub fn bytes_of_slice_mut(val: &mut [T]) -> &mut [u8] { let size = val.len().wrapping_mul(mem::size_of::()); // Safety: // Any alignment is allowed. // The size is determined in this function. // The Pod trait ensures the type is valid to cast to bytes. unsafe { slice::from_raw_parts_mut(val.as_mut_ptr().cast(), size) } } macro_rules! unsafe_impl_pod { ($($struct_name:ident),+ $(,)?) => { $( unsafe impl Pod for $struct_name { } )+ } } unsafe_impl_pod!(u8, u16, u32, u64); unsafe impl Pod for [T; N] {} #[cfg(test)] mod tests { use super::*; #[test] fn single() { let x = u32::to_be(0x0123_4567); let mut x_mut = x; let bytes = bytes_of(&x); let bytes_mut = bytes_of_mut(&mut x_mut); assert_eq!(bytes, [0x01, 0x23, 0x45, 0x67]); assert_eq!(bytes, bytes_mut); let x16 = [u16::to_be(0x0123), u16::to_be(0x4567)]; let (y, tail) = from_bytes::(bytes).unwrap(); let (y_mut, tail_mut) = from_bytes_mut::(bytes_mut).unwrap(); assert_eq!(*y, x); assert_eq!(y, y_mut); assert_eq!(tail, &[]); assert_eq!(tail, tail_mut); let (y, tail) = from_bytes::(bytes).unwrap(); let (y_mut, tail_mut) = from_bytes_mut::(bytes_mut).unwrap(); assert_eq!(*y, x16[0]); assert_eq!(y, y_mut); assert_eq!(tail, &bytes[2..]); assert_eq!(tail, tail_mut); let (y, tail) = from_bytes::(&bytes[2..]).unwrap(); let (y_mut, tail_mut) = from_bytes_mut::(&mut bytes_mut[2..]).unwrap(); assert_eq!(*y, x16[1]); assert_eq!(y, y_mut); assert_eq!(tail, &[]); assert_eq!(tail, tail_mut); assert_eq!(from_bytes::(&bytes[1..]), Err(())); assert_eq!(from_bytes::(&bytes[3..]), Err(())); assert_eq!(from_bytes::(&bytes[4..]), Err(())); assert_eq!(from_bytes_mut::(&mut bytes_mut[1..]), Err(())); assert_eq!(from_bytes_mut::(&mut bytes_mut[3..]), Err(())); assert_eq!(from_bytes_mut::(&mut bytes_mut[4..]), Err(())); } #[test] fn slice() { let x = [ u16::to_be(0x0123), u16::to_be(0x4567), u16::to_be(0x89ab), u16::to_be(0xcdef), ]; let mut x_mut = x; let bytes = bytes_of_slice(&x); let bytes_mut = bytes_of_slice_mut(&mut x_mut); assert_eq!(bytes, [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]); assert_eq!(bytes, bytes_mut); let (y, tail) = slice_from_bytes::(bytes, 4).unwrap(); let (y_mut, tail_mut) = slice_from_bytes_mut::(bytes_mut, 4).unwrap(); assert_eq!(y, x); assert_eq!(y, y_mut); assert_eq!(tail, &[]); assert_eq!(tail, tail_mut); let (y, tail) = slice_from_bytes::(&bytes[2..], 2).unwrap(); let (y_mut, tail_mut) = slice_from_bytes_mut::(&mut bytes_mut[2..], 2).unwrap(); assert_eq!(y, &x[1..3]); assert_eq!(y, y_mut); assert_eq!(tail, &bytes[6..]); assert_eq!(tail, tail_mut); assert_eq!(slice_from_bytes::(bytes, 5), Err(())); assert_eq!(slice_from_bytes::(&bytes[2..], 4), Err(())); assert_eq!(slice_from_bytes::(&bytes[1..], 2), Err(())); assert_eq!(slice_from_bytes_mut::(bytes_mut, 5), Err(())); assert_eq!(slice_from_bytes_mut::(&mut bytes_mut[2..], 4), Err(())); assert_eq!(slice_from_bytes_mut::(&mut bytes_mut[1..], 2), Err(())); } } object-0.36.5/src/read/any.rs000064400000000000000000001332631046102023000140340ustar 00000000000000use alloc::fmt; use alloc::vec::Vec; use core::marker::PhantomData; #[allow(unused_imports)] // Unused for Wasm use crate::endian::Endianness; #[cfg(feature = "coff")] use crate::read::coff; #[cfg(feature = "elf")] use crate::read::elf; #[cfg(feature = "macho")] use crate::read::macho; #[cfg(feature = "pe")] use crate::read::pe; #[cfg(feature = "wasm")] use crate::read::wasm; #[cfg(feature = "xcoff")] use crate::read::xcoff; use crate::read::{ self, Architecture, BinaryFormat, CodeView, ComdatKind, CompressedData, CompressedFileRange, Error, Export, FileFlags, FileKind, Import, Object, ObjectComdat, ObjectKind, ObjectMap, ObjectSection, ObjectSegment, ObjectSymbol, ObjectSymbolTable, ReadRef, Relocation, RelocationMap, Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags, SubArchitecture, SymbolFlags, SymbolIndex, SymbolKind, SymbolMap, SymbolMapName, SymbolScope, SymbolSection, }; /// Evaluate an expression on the contents of a file format enum. /// /// This is a hack to avoid virtual calls. macro_rules! with_inner { ($inner:expr, $enum:ident, | $var:ident | $body:expr) => { match $inner { #[cfg(feature = "coff")] $enum::Coff(ref $var) => $body, #[cfg(feature = "coff")] $enum::CoffBig(ref $var) => $body, #[cfg(feature = "elf")] $enum::Elf32(ref $var) => $body, #[cfg(feature = "elf")] $enum::Elf64(ref $var) => $body, #[cfg(feature = "macho")] $enum::MachO32(ref $var) => $body, #[cfg(feature = "macho")] $enum::MachO64(ref $var) => $body, #[cfg(feature = "pe")] $enum::Pe32(ref $var) => $body, #[cfg(feature = "pe")] $enum::Pe64(ref $var) => $body, #[cfg(feature = "wasm")] $enum::Wasm(ref $var) => $body, #[cfg(feature = "xcoff")] $enum::Xcoff32(ref $var) => $body, #[cfg(feature = "xcoff")] $enum::Xcoff64(ref $var) => $body, } }; } macro_rules! with_inner_mut { ($inner:expr, $enum:ident, | $var:ident | $body:expr) => { match $inner { #[cfg(feature = "coff")] $enum::Coff(ref mut $var) => $body, #[cfg(feature = "coff")] $enum::CoffBig(ref mut $var) => $body, #[cfg(feature = "elf")] $enum::Elf32(ref mut $var) => $body, #[cfg(feature = "elf")] $enum::Elf64(ref mut $var) => $body, #[cfg(feature = "macho")] $enum::MachO32(ref mut $var) => $body, #[cfg(feature = "macho")] $enum::MachO64(ref mut $var) => $body, #[cfg(feature = "pe")] $enum::Pe32(ref mut $var) => $body, #[cfg(feature = "pe")] $enum::Pe64(ref mut $var) => $body, #[cfg(feature = "wasm")] $enum::Wasm(ref mut $var) => $body, #[cfg(feature = "xcoff")] $enum::Xcoff32(ref mut $var) => $body, #[cfg(feature = "xcoff")] $enum::Xcoff64(ref mut $var) => $body, } }; } /// Like `with_inner!`, but wraps the result in another enum. macro_rules! map_inner { ($inner:expr, $from:ident, $to:ident, | $var:ident | $body:expr) => { match $inner { #[cfg(feature = "coff")] $from::Coff(ref $var) => $to::Coff($body), #[cfg(feature = "coff")] $from::CoffBig(ref $var) => $to::CoffBig($body), #[cfg(feature = "elf")] $from::Elf32(ref $var) => $to::Elf32($body), #[cfg(feature = "elf")] $from::Elf64(ref $var) => $to::Elf64($body), #[cfg(feature = "macho")] $from::MachO32(ref $var) => $to::MachO32($body), #[cfg(feature = "macho")] $from::MachO64(ref $var) => $to::MachO64($body), #[cfg(feature = "pe")] $from::Pe32(ref $var) => $to::Pe32($body), #[cfg(feature = "pe")] $from::Pe64(ref $var) => $to::Pe64($body), #[cfg(feature = "wasm")] $from::Wasm(ref $var) => $to::Wasm($body), #[cfg(feature = "xcoff")] $from::Xcoff32(ref $var) => $to::Xcoff32($body), #[cfg(feature = "xcoff")] $from::Xcoff64(ref $var) => $to::Xcoff64($body), } }; } /// Like `map_inner!`, but the result is a Result or Option. macro_rules! map_inner_option { ($inner:expr, $from:ident, $to:ident, | $var:ident | $body:expr) => { match $inner { #[cfg(feature = "coff")] $from::Coff(ref $var) => $body.map($to::Coff), #[cfg(feature = "coff")] $from::CoffBig(ref $var) => $body.map($to::CoffBig), #[cfg(feature = "elf")] $from::Elf32(ref $var) => $body.map($to::Elf32), #[cfg(feature = "elf")] $from::Elf64(ref $var) => $body.map($to::Elf64), #[cfg(feature = "macho")] $from::MachO32(ref $var) => $body.map($to::MachO32), #[cfg(feature = "macho")] $from::MachO64(ref $var) => $body.map($to::MachO64), #[cfg(feature = "pe")] $from::Pe32(ref $var) => $body.map($to::Pe32), #[cfg(feature = "pe")] $from::Pe64(ref $var) => $body.map($to::Pe64), #[cfg(feature = "wasm")] $from::Wasm(ref $var) => $body.map($to::Wasm), #[cfg(feature = "xcoff")] $from::Xcoff32(ref $var) => $body.map($to::Xcoff32), #[cfg(feature = "xcoff")] $from::Xcoff64(ref $var) => $body.map($to::Xcoff64), } }; } macro_rules! map_inner_option_mut { ($inner:expr, $from:ident, $to:ident, | $var:ident | $body:expr) => { match $inner { #[cfg(feature = "coff")] $from::Coff(ref mut $var) => $body.map($to::Coff), #[cfg(feature = "coff")] $from::CoffBig(ref mut $var) => $body.map($to::CoffBig), #[cfg(feature = "elf")] $from::Elf32(ref mut $var) => $body.map($to::Elf32), #[cfg(feature = "elf")] $from::Elf64(ref mut $var) => $body.map($to::Elf64), #[cfg(feature = "macho")] $from::MachO32(ref mut $var) => $body.map($to::MachO32), #[cfg(feature = "macho")] $from::MachO64(ref mut $var) => $body.map($to::MachO64), #[cfg(feature = "pe")] $from::Pe32(ref mut $var) => $body.map($to::Pe32), #[cfg(feature = "pe")] $from::Pe64(ref mut $var) => $body.map($to::Pe64), #[cfg(feature = "wasm")] $from::Wasm(ref mut $var) => $body.map($to::Wasm), #[cfg(feature = "xcoff")] $from::Xcoff32(ref mut $var) => $body.map($to::Xcoff32), #[cfg(feature = "xcoff")] $from::Xcoff64(ref mut $var) => $body.map($to::Xcoff64), } }; } /// Call `next` for a file format iterator. macro_rules! next_inner { ($inner:expr, $from:ident, $to:ident) => { match $inner { #[cfg(feature = "coff")] $from::Coff(ref mut iter) => iter.next().map($to::Coff), #[cfg(feature = "coff")] $from::CoffBig(ref mut iter) => iter.next().map($to::CoffBig), #[cfg(feature = "elf")] $from::Elf32(ref mut iter) => iter.next().map($to::Elf32), #[cfg(feature = "elf")] $from::Elf64(ref mut iter) => iter.next().map($to::Elf64), #[cfg(feature = "macho")] $from::MachO32(ref mut iter) => iter.next().map($to::MachO32), #[cfg(feature = "macho")] $from::MachO64(ref mut iter) => iter.next().map($to::MachO64), #[cfg(feature = "pe")] $from::Pe32(ref mut iter) => iter.next().map($to::Pe32), #[cfg(feature = "pe")] $from::Pe64(ref mut iter) => iter.next().map($to::Pe64), #[cfg(feature = "wasm")] $from::Wasm(ref mut iter) => iter.next().map($to::Wasm), #[cfg(feature = "xcoff")] $from::Xcoff32(ref mut iter) => iter.next().map($to::Xcoff32), #[cfg(feature = "xcoff")] $from::Xcoff64(ref mut iter) => iter.next().map($to::Xcoff64), } }; } /// An object file that can be any supported file format. /// /// Most functionality is provided by the [`Object`] trait implementation. #[derive(Debug)] #[non_exhaustive] #[allow(missing_docs)] pub enum File<'data, R: ReadRef<'data> = &'data [u8]> { #[cfg(feature = "coff")] Coff(coff::CoffFile<'data, R>), #[cfg(feature = "coff")] CoffBig(coff::CoffBigFile<'data, R>), #[cfg(feature = "elf")] Elf32(elf::ElfFile32<'data, Endianness, R>), #[cfg(feature = "elf")] Elf64(elf::ElfFile64<'data, Endianness, R>), #[cfg(feature = "macho")] MachO32(macho::MachOFile32<'data, Endianness, R>), #[cfg(feature = "macho")] MachO64(macho::MachOFile64<'data, Endianness, R>), #[cfg(feature = "pe")] Pe32(pe::PeFile32<'data, R>), #[cfg(feature = "pe")] Pe64(pe::PeFile64<'data, R>), #[cfg(feature = "wasm")] Wasm(wasm::WasmFile<'data, R>), #[cfg(feature = "xcoff")] Xcoff32(xcoff::XcoffFile32<'data, R>), #[cfg(feature = "xcoff")] Xcoff64(xcoff::XcoffFile64<'data, R>), } impl<'data, R: ReadRef<'data>> File<'data, R> { /// Parse the raw file data. pub fn parse(data: R) -> Result { Ok(match FileKind::parse(data)? { #[cfg(feature = "elf")] FileKind::Elf32 => File::Elf32(elf::ElfFile32::parse(data)?), #[cfg(feature = "elf")] FileKind::Elf64 => File::Elf64(elf::ElfFile64::parse(data)?), #[cfg(feature = "macho")] FileKind::MachO32 => File::MachO32(macho::MachOFile32::parse(data)?), #[cfg(feature = "macho")] FileKind::MachO64 => File::MachO64(macho::MachOFile64::parse(data)?), #[cfg(feature = "wasm")] FileKind::Wasm => File::Wasm(wasm::WasmFile::parse(data)?), #[cfg(feature = "pe")] FileKind::Pe32 => File::Pe32(pe::PeFile32::parse(data)?), #[cfg(feature = "pe")] FileKind::Pe64 => File::Pe64(pe::PeFile64::parse(data)?), #[cfg(feature = "coff")] FileKind::Coff => File::Coff(coff::CoffFile::parse(data)?), #[cfg(feature = "coff")] FileKind::CoffBig => File::CoffBig(coff::CoffBigFile::parse(data)?), #[cfg(feature = "xcoff")] FileKind::Xcoff32 => File::Xcoff32(xcoff::XcoffFile32::parse(data)?), #[cfg(feature = "xcoff")] FileKind::Xcoff64 => File::Xcoff64(xcoff::XcoffFile64::parse(data)?), #[allow(unreachable_patterns)] _ => return Err(Error("Unsupported file format")), }) } /// Parse a Mach-O image from the dyld shared cache. #[cfg(feature = "macho")] pub fn parse_dyld_cache_image<'cache, E: crate::Endian>( image: &macho::DyldCacheImage<'data, 'cache, E, R>, ) -> Result { Ok(match image.cache.architecture().address_size() { Some(read::AddressSize::U64) => { File::MachO64(macho::MachOFile64::parse_dyld_cache_image(image)?) } Some(read::AddressSize::U32) => { File::MachO32(macho::MachOFile32::parse_dyld_cache_image(image)?) } _ => return Err(Error("Unsupported file format")), }) } /// Return the file format. pub fn format(&self) -> BinaryFormat { match self { #[cfg(feature = "coff")] File::Coff(_) | File::CoffBig(_) => BinaryFormat::Coff, #[cfg(feature = "elf")] File::Elf32(_) | File::Elf64(_) => BinaryFormat::Elf, #[cfg(feature = "macho")] File::MachO32(_) | File::MachO64(_) => BinaryFormat::MachO, #[cfg(feature = "pe")] File::Pe32(_) | File::Pe64(_) => BinaryFormat::Pe, #[cfg(feature = "wasm")] File::Wasm(_) => BinaryFormat::Wasm, #[cfg(feature = "xcoff")] File::Xcoff32(_) | File::Xcoff64(_) => BinaryFormat::Xcoff, } } } impl<'data, R: ReadRef<'data>> read::private::Sealed for File<'data, R> {} impl<'data, R> Object<'data> for File<'data, R> where R: ReadRef<'data>, { type Segment<'file> = Segment<'data, 'file, R> where Self: 'file, 'data: 'file; type SegmentIterator<'file> = SegmentIterator<'data, 'file, R> where Self: 'file, 'data: 'file; type Section<'file> = Section<'data, 'file, R> where Self: 'file, 'data: 'file; type SectionIterator<'file> = SectionIterator<'data, 'file, R> where Self: 'file, 'data: 'file; type Comdat<'file> = Comdat<'data, 'file, R> where Self: 'file, 'data: 'file; type ComdatIterator<'file> = ComdatIterator<'data, 'file, R> where Self: 'file, 'data: 'file; type Symbol<'file> = Symbol<'data, 'file, R> where Self: 'file, 'data: 'file; type SymbolIterator<'file> = SymbolIterator<'data, 'file, R> where Self: 'file, 'data: 'file; type SymbolTable<'file> = SymbolTable<'data, 'file, R> where Self: 'file, 'data: 'file; type DynamicRelocationIterator<'file> = DynamicRelocationIterator<'data, 'file, R> where Self: 'file, 'data: 'file; fn architecture(&self) -> Architecture { with_inner!(self, File, |x| x.architecture()) } fn sub_architecture(&self) -> Option { with_inner!(self, File, |x| x.sub_architecture()) } fn is_little_endian(&self) -> bool { with_inner!(self, File, |x| x.is_little_endian()) } fn is_64(&self) -> bool { with_inner!(self, File, |x| x.is_64()) } fn kind(&self) -> ObjectKind { with_inner!(self, File, |x| x.kind()) } fn segments(&self) -> SegmentIterator<'data, '_, R> { SegmentIterator { inner: map_inner!(self, File, SegmentIteratorInternal, |x| x.segments()), } } fn section_by_name_bytes<'file>( &'file self, section_name: &[u8], ) -> Option> { map_inner_option!(self, File, SectionInternal, |x| x .section_by_name_bytes(section_name)) .map(|inner| Section { inner }) } fn section_by_index(&self, index: SectionIndex) -> Result> { map_inner_option!(self, File, SectionInternal, |x| x.section_by_index(index)) .map(|inner| Section { inner }) } fn sections(&self) -> SectionIterator<'data, '_, R> { SectionIterator { inner: map_inner!(self, File, SectionIteratorInternal, |x| x.sections()), } } fn comdats(&self) -> ComdatIterator<'data, '_, R> { ComdatIterator { inner: map_inner!(self, File, ComdatIteratorInternal, |x| x.comdats()), } } fn symbol_by_index(&self, index: SymbolIndex) -> Result> { map_inner_option!(self, File, SymbolInternal, |x| x .symbol_by_index(index) .map(|x| (x, PhantomData))) .map(|inner| Symbol { inner }) } fn symbols(&self) -> SymbolIterator<'data, '_, R> { SymbolIterator { inner: map_inner!(self, File, SymbolIteratorInternal, |x| ( x.symbols(), PhantomData )), } } fn symbol_table(&self) -> Option> { map_inner_option!(self, File, SymbolTableInternal, |x| x .symbol_table() .map(|x| (x, PhantomData))) .map(|inner| SymbolTable { inner }) } fn dynamic_symbols(&self) -> SymbolIterator<'data, '_, R> { SymbolIterator { inner: map_inner!(self, File, SymbolIteratorInternal, |x| ( x.dynamic_symbols(), PhantomData )), } } fn dynamic_symbol_table(&self) -> Option> { map_inner_option!(self, File, SymbolTableInternal, |x| x .dynamic_symbol_table() .map(|x| (x, PhantomData))) .map(|inner| SymbolTable { inner }) } #[cfg(feature = "elf")] fn dynamic_relocations(&self) -> Option> { let inner = match self { File::Elf32(ref elf) => { DynamicRelocationIteratorInternal::Elf32(elf.dynamic_relocations()?) } File::Elf64(ref elf) => { DynamicRelocationIteratorInternal::Elf64(elf.dynamic_relocations()?) } #[allow(unreachable_patterns)] _ => return None, }; Some(DynamicRelocationIterator { inner }) } #[cfg(not(feature = "elf"))] fn dynamic_relocations(&self) -> Option> { None } fn symbol_map(&self) -> SymbolMap> { with_inner!(self, File, |x| x.symbol_map()) } fn object_map(&self) -> ObjectMap<'data> { with_inner!(self, File, |x| x.object_map()) } fn imports(&self) -> Result>> { with_inner!(self, File, |x| x.imports()) } fn exports(&self) -> Result>> { with_inner!(self, File, |x| x.exports()) } fn has_debug_symbols(&self) -> bool { with_inner!(self, File, |x| x.has_debug_symbols()) } #[inline] fn mach_uuid(&self) -> Result> { with_inner!(self, File, |x| x.mach_uuid()) } #[inline] fn build_id(&self) -> Result> { with_inner!(self, File, |x| x.build_id()) } #[inline] fn gnu_debuglink(&self) -> Result> { with_inner!(self, File, |x| x.gnu_debuglink()) } #[inline] fn gnu_debugaltlink(&self) -> Result> { with_inner!(self, File, |x| x.gnu_debugaltlink()) } #[inline] fn pdb_info(&self) -> Result>> { with_inner!(self, File, |x| x.pdb_info()) } fn relative_address_base(&self) -> u64 { with_inner!(self, File, |x| x.relative_address_base()) } fn entry(&self) -> u64 { with_inner!(self, File, |x| x.entry()) } fn flags(&self) -> FileFlags { with_inner!(self, File, |x| x.flags()) } } /// An iterator for the loadable segments in a [`File`]. #[derive(Debug)] pub struct SegmentIterator<'data, 'file, R: ReadRef<'data> = &'data [u8]> { inner: SegmentIteratorInternal<'data, 'file, R>, } #[derive(Debug)] enum SegmentIteratorInternal<'data, 'file, R: ReadRef<'data>> { #[cfg(feature = "coff")] Coff(coff::CoffSegmentIterator<'data, 'file, R>), #[cfg(feature = "coff")] CoffBig(coff::CoffBigSegmentIterator<'data, 'file, R>), #[cfg(feature = "elf")] Elf32(elf::ElfSegmentIterator32<'data, 'file, Endianness, R>), #[cfg(feature = "elf")] Elf64(elf::ElfSegmentIterator64<'data, 'file, Endianness, R>), #[cfg(feature = "macho")] MachO32(macho::MachOSegmentIterator32<'data, 'file, Endianness, R>), #[cfg(feature = "macho")] MachO64(macho::MachOSegmentIterator64<'data, 'file, Endianness, R>), #[cfg(feature = "pe")] Pe32(pe::PeSegmentIterator32<'data, 'file, R>), #[cfg(feature = "pe")] Pe64(pe::PeSegmentIterator64<'data, 'file, R>), #[cfg(feature = "wasm")] Wasm(wasm::WasmSegmentIterator<'data, 'file, R>), #[cfg(feature = "xcoff")] Xcoff32(xcoff::XcoffSegmentIterator32<'data, 'file, R>), #[cfg(feature = "xcoff")] Xcoff64(xcoff::XcoffSegmentIterator64<'data, 'file, R>), } impl<'data, 'file, R: ReadRef<'data>> Iterator for SegmentIterator<'data, 'file, R> { type Item = Segment<'data, 'file, R>; fn next(&mut self) -> Option { next_inner!(self.inner, SegmentIteratorInternal, SegmentInternal) .map(|inner| Segment { inner }) } } /// A loadable segment in a [`File`]. /// /// Most functionality is provided by the [`ObjectSegment`] trait implementation. pub struct Segment<'data, 'file, R: ReadRef<'data> = &'data [u8]> { inner: SegmentInternal<'data, 'file, R>, } #[derive(Debug)] enum SegmentInternal<'data, 'file, R: ReadRef<'data>> { #[cfg(feature = "coff")] Coff(coff::CoffSegment<'data, 'file, R>), #[cfg(feature = "coff")] CoffBig(coff::CoffBigSegment<'data, 'file, R>), #[cfg(feature = "elf")] Elf32(elf::ElfSegment32<'data, 'file, Endianness, R>), #[cfg(feature = "elf")] Elf64(elf::ElfSegment64<'data, 'file, Endianness, R>), #[cfg(feature = "macho")] MachO32(macho::MachOSegment32<'data, 'file, Endianness, R>), #[cfg(feature = "macho")] MachO64(macho::MachOSegment64<'data, 'file, Endianness, R>), #[cfg(feature = "pe")] Pe32(pe::PeSegment32<'data, 'file, R>), #[cfg(feature = "pe")] Pe64(pe::PeSegment64<'data, 'file, R>), #[cfg(feature = "wasm")] Wasm(wasm::WasmSegment<'data, 'file, R>), #[cfg(feature = "xcoff")] Xcoff32(xcoff::XcoffSegment32<'data, 'file, R>), #[cfg(feature = "xcoff")] Xcoff64(xcoff::XcoffSegment64<'data, 'file, R>), } impl<'data, 'file, R: ReadRef<'data>> fmt::Debug for Segment<'data, 'file, R> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // It's painful to do much better than this let mut s = f.debug_struct("Segment"); match self.name() { Ok(Some(ref name)) => { s.field("name", name); } Ok(None) => {} Err(_) => { s.field("name", &""); } } s.field("address", &self.address()) .field("size", &self.size()) .finish() } } impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for Segment<'data, 'file, R> {} impl<'data, 'file, R: ReadRef<'data>> ObjectSegment<'data> for Segment<'data, 'file, R> { fn address(&self) -> u64 { with_inner!(self.inner, SegmentInternal, |x| x.address()) } fn size(&self) -> u64 { with_inner!(self.inner, SegmentInternal, |x| x.size()) } fn align(&self) -> u64 { with_inner!(self.inner, SegmentInternal, |x| x.align()) } fn file_range(&self) -> (u64, u64) { with_inner!(self.inner, SegmentInternal, |x| x.file_range()) } fn data(&self) -> Result<&'data [u8]> { with_inner!(self.inner, SegmentInternal, |x| x.data()) } fn data_range(&self, address: u64, size: u64) -> Result> { with_inner!(self.inner, SegmentInternal, |x| x.data_range(address, size)) } fn name_bytes(&self) -> Result> { with_inner!(self.inner, SegmentInternal, |x| x.name_bytes()) } fn name(&self) -> Result> { with_inner!(self.inner, SegmentInternal, |x| x.name()) } fn flags(&self) -> SegmentFlags { with_inner!(self.inner, SegmentInternal, |x| x.flags()) } } /// An iterator for the sections in a [`File`]. #[derive(Debug)] pub struct SectionIterator<'data, 'file, R: ReadRef<'data> = &'data [u8]> { inner: SectionIteratorInternal<'data, 'file, R>, } // we wrap our enums in a struct so that they are kept private. #[derive(Debug)] enum SectionIteratorInternal<'data, 'file, R: ReadRef<'data>> { #[cfg(feature = "coff")] Coff(coff::CoffSectionIterator<'data, 'file, R>), #[cfg(feature = "coff")] CoffBig(coff::CoffBigSectionIterator<'data, 'file, R>), #[cfg(feature = "elf")] Elf32(elf::ElfSectionIterator32<'data, 'file, Endianness, R>), #[cfg(feature = "elf")] Elf64(elf::ElfSectionIterator64<'data, 'file, Endianness, R>), #[cfg(feature = "macho")] MachO32(macho::MachOSectionIterator32<'data, 'file, Endianness, R>), #[cfg(feature = "macho")] MachO64(macho::MachOSectionIterator64<'data, 'file, Endianness, R>), #[cfg(feature = "pe")] Pe32(pe::PeSectionIterator32<'data, 'file, R>), #[cfg(feature = "pe")] Pe64(pe::PeSectionIterator64<'data, 'file, R>), #[cfg(feature = "wasm")] Wasm(wasm::WasmSectionIterator<'data, 'file, R>), #[cfg(feature = "xcoff")] Xcoff32(xcoff::XcoffSectionIterator32<'data, 'file, R>), #[cfg(feature = "xcoff")] Xcoff64(xcoff::XcoffSectionIterator64<'data, 'file, R>), } impl<'data, 'file, R: ReadRef<'data>> Iterator for SectionIterator<'data, 'file, R> { type Item = Section<'data, 'file, R>; fn next(&mut self) -> Option { next_inner!(self.inner, SectionIteratorInternal, SectionInternal) .map(|inner| Section { inner }) } } /// A section in a [`File`]. /// /// Most functionality is provided by the [`ObjectSection`] trait implementation. pub struct Section<'data, 'file, R: ReadRef<'data> = &'data [u8]> { inner: SectionInternal<'data, 'file, R>, } enum SectionInternal<'data, 'file, R: ReadRef<'data>> { #[cfg(feature = "coff")] Coff(coff::CoffSection<'data, 'file, R>), #[cfg(feature = "coff")] CoffBig(coff::CoffBigSection<'data, 'file, R>), #[cfg(feature = "elf")] Elf32(elf::ElfSection32<'data, 'file, Endianness, R>), #[cfg(feature = "elf")] Elf64(elf::ElfSection64<'data, 'file, Endianness, R>), #[cfg(feature = "macho")] MachO32(macho::MachOSection32<'data, 'file, Endianness, R>), #[cfg(feature = "macho")] MachO64(macho::MachOSection64<'data, 'file, Endianness, R>), #[cfg(feature = "pe")] Pe32(pe::PeSection32<'data, 'file, R>), #[cfg(feature = "pe")] Pe64(pe::PeSection64<'data, 'file, R>), #[cfg(feature = "wasm")] Wasm(wasm::WasmSection<'data, 'file, R>), #[cfg(feature = "xcoff")] Xcoff32(xcoff::XcoffSection32<'data, 'file, R>), #[cfg(feature = "xcoff")] Xcoff64(xcoff::XcoffSection64<'data, 'file, R>), } impl<'data, 'file, R: ReadRef<'data>> fmt::Debug for Section<'data, 'file, R> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // It's painful to do much better than this let mut s = f.debug_struct("Section"); match self.segment_name() { Ok(Some(ref name)) => { s.field("segment", name); } Ok(None) => {} Err(_) => { s.field("segment", &""); } } s.field("name", &self.name().unwrap_or("")) .field("address", &self.address()) .field("size", &self.size()) .field("align", &self.align()) .field("kind", &self.kind()) .field("flags", &self.flags()) .finish() } } impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for Section<'data, 'file, R> {} impl<'data, 'file, R: ReadRef<'data>> ObjectSection<'data> for Section<'data, 'file, R> { type RelocationIterator = SectionRelocationIterator<'data, 'file, R>; fn index(&self) -> SectionIndex { with_inner!(self.inner, SectionInternal, |x| x.index()) } fn address(&self) -> u64 { with_inner!(self.inner, SectionInternal, |x| x.address()) } fn size(&self) -> u64 { with_inner!(self.inner, SectionInternal, |x| x.size()) } fn align(&self) -> u64 { with_inner!(self.inner, SectionInternal, |x| x.align()) } fn file_range(&self) -> Option<(u64, u64)> { with_inner!(self.inner, SectionInternal, |x| x.file_range()) } fn data(&self) -> Result<&'data [u8]> { with_inner!(self.inner, SectionInternal, |x| x.data()) } fn data_range(&self, address: u64, size: u64) -> Result> { with_inner!(self.inner, SectionInternal, |x| x.data_range(address, size)) } fn compressed_file_range(&self) -> Result { with_inner!(self.inner, SectionInternal, |x| x.compressed_file_range()) } fn compressed_data(&self) -> Result> { with_inner!(self.inner, SectionInternal, |x| x.compressed_data()) } fn name_bytes(&self) -> Result<&'data [u8]> { with_inner!(self.inner, SectionInternal, |x| x.name_bytes()) } fn name(&self) -> Result<&'data str> { with_inner!(self.inner, SectionInternal, |x| x.name()) } fn segment_name_bytes(&self) -> Result> { with_inner!(self.inner, SectionInternal, |x| x.segment_name_bytes()) } fn segment_name(&self) -> Result> { with_inner!(self.inner, SectionInternal, |x| x.segment_name()) } fn kind(&self) -> SectionKind { with_inner!(self.inner, SectionInternal, |x| x.kind()) } fn relocations(&self) -> SectionRelocationIterator<'data, 'file, R> { SectionRelocationIterator { inner: map_inner!( self.inner, SectionInternal, SectionRelocationIteratorInternal, |x| x.relocations() ), } } fn relocation_map(&self) -> Result { with_inner!(self.inner, SectionInternal, |x| x.relocation_map()) } fn flags(&self) -> SectionFlags { with_inner!(self.inner, SectionInternal, |x| x.flags()) } } /// An iterator for the COMDAT section groups in a [`File`]. #[derive(Debug)] pub struct ComdatIterator<'data, 'file, R: ReadRef<'data> = &'data [u8]> { inner: ComdatIteratorInternal<'data, 'file, R>, } #[derive(Debug)] enum ComdatIteratorInternal<'data, 'file, R: ReadRef<'data>> { #[cfg(feature = "coff")] Coff(coff::CoffComdatIterator<'data, 'file, R>), #[cfg(feature = "coff")] CoffBig(coff::CoffBigComdatIterator<'data, 'file, R>), #[cfg(feature = "elf")] Elf32(elf::ElfComdatIterator32<'data, 'file, Endianness, R>), #[cfg(feature = "elf")] Elf64(elf::ElfComdatIterator64<'data, 'file, Endianness, R>), #[cfg(feature = "macho")] MachO32(macho::MachOComdatIterator32<'data, 'file, Endianness, R>), #[cfg(feature = "macho")] MachO64(macho::MachOComdatIterator64<'data, 'file, Endianness, R>), #[cfg(feature = "pe")] Pe32(pe::PeComdatIterator32<'data, 'file, R>), #[cfg(feature = "pe")] Pe64(pe::PeComdatIterator64<'data, 'file, R>), #[cfg(feature = "wasm")] Wasm(wasm::WasmComdatIterator<'data, 'file, R>), #[cfg(feature = "xcoff")] Xcoff32(xcoff::XcoffComdatIterator32<'data, 'file, R>), #[cfg(feature = "xcoff")] Xcoff64(xcoff::XcoffComdatIterator64<'data, 'file, R>), } impl<'data, 'file, R: ReadRef<'data>> Iterator for ComdatIterator<'data, 'file, R> { type Item = Comdat<'data, 'file, R>; fn next(&mut self) -> Option { next_inner!(self.inner, ComdatIteratorInternal, ComdatInternal) .map(|inner| Comdat { inner }) } } /// A COMDAT section group in a [`File`]. /// /// Most functionality is provided by the [`ObjectComdat`] trait implementation. pub struct Comdat<'data, 'file, R: ReadRef<'data> = &'data [u8]> { inner: ComdatInternal<'data, 'file, R>, } enum ComdatInternal<'data, 'file, R: ReadRef<'data>> { #[cfg(feature = "coff")] Coff(coff::CoffComdat<'data, 'file, R>), #[cfg(feature = "coff")] CoffBig(coff::CoffBigComdat<'data, 'file, R>), #[cfg(feature = "elf")] Elf32(elf::ElfComdat32<'data, 'file, Endianness, R>), #[cfg(feature = "elf")] Elf64(elf::ElfComdat64<'data, 'file, Endianness, R>), #[cfg(feature = "macho")] MachO32(macho::MachOComdat32<'data, 'file, Endianness, R>), #[cfg(feature = "macho")] MachO64(macho::MachOComdat64<'data, 'file, Endianness, R>), #[cfg(feature = "pe")] Pe32(pe::PeComdat32<'data, 'file, R>), #[cfg(feature = "pe")] Pe64(pe::PeComdat64<'data, 'file, R>), #[cfg(feature = "wasm")] Wasm(wasm::WasmComdat<'data, 'file, R>), #[cfg(feature = "xcoff")] Xcoff32(xcoff::XcoffComdat32<'data, 'file, R>), #[cfg(feature = "xcoff")] Xcoff64(xcoff::XcoffComdat64<'data, 'file, R>), } impl<'data, 'file, R: ReadRef<'data>> fmt::Debug for Comdat<'data, 'file, R> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut s = f.debug_struct("Comdat"); s.field("symbol", &self.symbol()) .field("name", &self.name().unwrap_or("")) .field("kind", &self.kind()) .finish() } } impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for Comdat<'data, 'file, R> {} impl<'data, 'file, R: ReadRef<'data>> ObjectComdat<'data> for Comdat<'data, 'file, R> { type SectionIterator = ComdatSectionIterator<'data, 'file, R>; fn kind(&self) -> ComdatKind { with_inner!(self.inner, ComdatInternal, |x| x.kind()) } fn symbol(&self) -> SymbolIndex { with_inner!(self.inner, ComdatInternal, |x| x.symbol()) } fn name_bytes(&self) -> Result<&'data [u8]> { with_inner!(self.inner, ComdatInternal, |x| x.name_bytes()) } fn name(&self) -> Result<&'data str> { with_inner!(self.inner, ComdatInternal, |x| x.name()) } fn sections(&self) -> ComdatSectionIterator<'data, 'file, R> { ComdatSectionIterator { inner: map_inner!( self.inner, ComdatInternal, ComdatSectionIteratorInternal, |x| x.sections() ), } } } /// An iterator for the sections in a [`Comdat`]. #[derive(Debug)] pub struct ComdatSectionIterator<'data, 'file, R: ReadRef<'data> = &'data [u8]> { inner: ComdatSectionIteratorInternal<'data, 'file, R>, } #[derive(Debug)] enum ComdatSectionIteratorInternal<'data, 'file, R: ReadRef<'data>> { #[cfg(feature = "coff")] Coff(coff::CoffComdatSectionIterator<'data, 'file, R>), #[cfg(feature = "coff")] CoffBig(coff::CoffBigComdatSectionIterator<'data, 'file, R>), #[cfg(feature = "elf")] Elf32(elf::ElfComdatSectionIterator32<'data, 'file, Endianness, R>), #[cfg(feature = "elf")] Elf64(elf::ElfComdatSectionIterator64<'data, 'file, Endianness, R>), #[cfg(feature = "macho")] MachO32(macho::MachOComdatSectionIterator32<'data, 'file, Endianness, R>), #[cfg(feature = "macho")] MachO64(macho::MachOComdatSectionIterator64<'data, 'file, Endianness, R>), #[cfg(feature = "pe")] Pe32(pe::PeComdatSectionIterator32<'data, 'file, R>), #[cfg(feature = "pe")] Pe64(pe::PeComdatSectionIterator64<'data, 'file, R>), #[cfg(feature = "wasm")] Wasm(wasm::WasmComdatSectionIterator<'data, 'file, R>), #[cfg(feature = "xcoff")] Xcoff32(xcoff::XcoffComdatSectionIterator32<'data, 'file, R>), #[cfg(feature = "xcoff")] Xcoff64(xcoff::XcoffComdatSectionIterator64<'data, 'file, R>), } impl<'data, 'file, R: ReadRef<'data>> Iterator for ComdatSectionIterator<'data, 'file, R> { type Item = SectionIndex; fn next(&mut self) -> Option { with_inner_mut!(self.inner, ComdatSectionIteratorInternal, |x| x.next()) } } /// A symbol table in a [`File`]. /// /// Most functionality is provided by the [`ObjectSymbolTable`] trait implementation. #[derive(Debug)] pub struct SymbolTable<'data, 'file, R = &'data [u8]> where R: ReadRef<'data>, { inner: SymbolTableInternal<'data, 'file, R>, } #[derive(Debug)] enum SymbolTableInternal<'data, 'file, R> where R: ReadRef<'data>, { #[cfg(feature = "coff")] Coff((coff::CoffSymbolTable<'data, 'file, R>, PhantomData)), #[cfg(feature = "coff")] CoffBig((coff::CoffBigSymbolTable<'data, 'file, R>, PhantomData)), #[cfg(feature = "elf")] Elf32( ( elf::ElfSymbolTable32<'data, 'file, Endianness, R>, PhantomData, ), ), #[cfg(feature = "elf")] Elf64( ( elf::ElfSymbolTable64<'data, 'file, Endianness, R>, PhantomData, ), ), #[cfg(feature = "macho")] MachO32( ( macho::MachOSymbolTable32<'data, 'file, Endianness, R>, PhantomData<()>, ), ), #[cfg(feature = "macho")] MachO64( ( macho::MachOSymbolTable64<'data, 'file, Endianness, R>, PhantomData<()>, ), ), #[cfg(feature = "pe")] Pe32((coff::CoffSymbolTable<'data, 'file, R>, PhantomData)), #[cfg(feature = "pe")] Pe64((coff::CoffSymbolTable<'data, 'file, R>, PhantomData)), #[cfg(feature = "wasm")] Wasm((wasm::WasmSymbolTable<'data, 'file>, PhantomData)), #[cfg(feature = "xcoff")] Xcoff32((xcoff::XcoffSymbolTable32<'data, 'file, R>, PhantomData)), #[cfg(feature = "xcoff")] Xcoff64((xcoff::XcoffSymbolTable64<'data, 'file, R>, PhantomData)), } impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for SymbolTable<'data, 'file, R> {} impl<'data, 'file, R: ReadRef<'data>> ObjectSymbolTable<'data> for SymbolTable<'data, 'file, R> { type Symbol = Symbol<'data, 'file, R>; type SymbolIterator = SymbolIterator<'data, 'file, R>; fn symbols(&self) -> Self::SymbolIterator { SymbolIterator { inner: map_inner!( self.inner, SymbolTableInternal, SymbolIteratorInternal, |x| (x.0.symbols(), PhantomData) ), } } fn symbol_by_index(&self, index: SymbolIndex) -> Result { map_inner_option!(self.inner, SymbolTableInternal, SymbolInternal, |x| x .0 .symbol_by_index(index) .map(|x| (x, PhantomData))) .map(|inner| Symbol { inner }) } } /// An iterator for the symbols in a [`SymbolTable`]. #[derive(Debug)] pub struct SymbolIterator<'data, 'file, R = &'data [u8]> where R: ReadRef<'data>, { inner: SymbolIteratorInternal<'data, 'file, R>, } #[derive(Debug)] enum SymbolIteratorInternal<'data, 'file, R> where R: ReadRef<'data>, { #[cfg(feature = "coff")] Coff((coff::CoffSymbolIterator<'data, 'file, R>, PhantomData)), #[cfg(feature = "coff")] CoffBig((coff::CoffBigSymbolIterator<'data, 'file, R>, PhantomData)), #[cfg(feature = "elf")] Elf32( ( elf::ElfSymbolIterator32<'data, 'file, Endianness, R>, PhantomData, ), ), #[cfg(feature = "elf")] Elf64( ( elf::ElfSymbolIterator64<'data, 'file, Endianness, R>, PhantomData, ), ), #[cfg(feature = "macho")] MachO32( ( macho::MachOSymbolIterator32<'data, 'file, Endianness, R>, PhantomData<()>, ), ), #[cfg(feature = "macho")] MachO64( ( macho::MachOSymbolIterator64<'data, 'file, Endianness, R>, PhantomData<()>, ), ), #[cfg(feature = "pe")] Pe32((coff::CoffSymbolIterator<'data, 'file, R>, PhantomData)), #[cfg(feature = "pe")] Pe64((coff::CoffSymbolIterator<'data, 'file, R>, PhantomData)), #[cfg(feature = "wasm")] Wasm((wasm::WasmSymbolIterator<'data, 'file>, PhantomData)), #[cfg(feature = "xcoff")] Xcoff32( ( xcoff::XcoffSymbolIterator32<'data, 'file, R>, PhantomData, ), ), #[cfg(feature = "xcoff")] Xcoff64( ( xcoff::XcoffSymbolIterator64<'data, 'file, R>, PhantomData, ), ), } impl<'data, 'file, R: ReadRef<'data>> Iterator for SymbolIterator<'data, 'file, R> { type Item = Symbol<'data, 'file, R>; fn next(&mut self) -> Option { map_inner_option_mut!(self.inner, SymbolIteratorInternal, SymbolInternal, |iter| { iter.0.next().map(|x| (x, PhantomData)) }) .map(|inner| Symbol { inner }) } } /// An symbol in a [`SymbolTable`]. /// /// Most functionality is provided by the [`ObjectSymbol`] trait implementation. pub struct Symbol<'data, 'file, R = &'data [u8]> where R: ReadRef<'data>, { inner: SymbolInternal<'data, 'file, R>, } enum SymbolInternal<'data, 'file, R> where R: ReadRef<'data>, { #[cfg(feature = "coff")] Coff((coff::CoffSymbol<'data, 'file, R>, PhantomData)), #[cfg(feature = "coff")] CoffBig((coff::CoffBigSymbol<'data, 'file, R>, PhantomData)), #[cfg(feature = "elf")] Elf32( ( elf::ElfSymbol32<'data, 'file, Endianness, R>, PhantomData, ), ), #[cfg(feature = "elf")] Elf64( ( elf::ElfSymbol64<'data, 'file, Endianness, R>, PhantomData, ), ), #[cfg(feature = "macho")] MachO32( ( macho::MachOSymbol32<'data, 'file, Endianness, R>, PhantomData<()>, ), ), #[cfg(feature = "macho")] MachO64( ( macho::MachOSymbol64<'data, 'file, Endianness, R>, PhantomData<()>, ), ), #[cfg(feature = "pe")] Pe32((coff::CoffSymbol<'data, 'file, R>, PhantomData)), #[cfg(feature = "pe")] Pe64((coff::CoffSymbol<'data, 'file, R>, PhantomData)), #[cfg(feature = "wasm")] Wasm((wasm::WasmSymbol<'data, 'file>, PhantomData)), #[cfg(feature = "xcoff")] Xcoff32((xcoff::XcoffSymbol32<'data, 'file, R>, PhantomData)), #[cfg(feature = "xcoff")] Xcoff64((xcoff::XcoffSymbol64<'data, 'file, R>, PhantomData)), } impl<'data, 'file, R: ReadRef<'data>> fmt::Debug for Symbol<'data, 'file, R> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Symbol") .field("name", &self.name().unwrap_or("")) .field("address", &self.address()) .field("size", &self.size()) .field("kind", &self.kind()) .field("section", &self.section()) .field("scope", &self.scope()) .field("weak", &self.is_weak()) .field("flags", &self.flags()) .finish() } } impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for Symbol<'data, 'file, R> {} impl<'data, 'file, R: ReadRef<'data>> ObjectSymbol<'data> for Symbol<'data, 'file, R> { fn index(&self) -> SymbolIndex { with_inner!(self.inner, SymbolInternal, |x| x.0.index()) } fn name_bytes(&self) -> Result<&'data [u8]> { with_inner!(self.inner, SymbolInternal, |x| x.0.name_bytes()) } fn name(&self) -> Result<&'data str> { with_inner!(self.inner, SymbolInternal, |x| x.0.name()) } fn address(&self) -> u64 { with_inner!(self.inner, SymbolInternal, |x| x.0.address()) } fn size(&self) -> u64 { with_inner!(self.inner, SymbolInternal, |x| x.0.size()) } fn kind(&self) -> SymbolKind { with_inner!(self.inner, SymbolInternal, |x| x.0.kind()) } fn section(&self) -> SymbolSection { with_inner!(self.inner, SymbolInternal, |x| x.0.section()) } fn is_undefined(&self) -> bool { with_inner!(self.inner, SymbolInternal, |x| x.0.is_undefined()) } fn is_definition(&self) -> bool { with_inner!(self.inner, SymbolInternal, |x| x.0.is_definition()) } fn is_common(&self) -> bool { with_inner!(self.inner, SymbolInternal, |x| x.0.is_common()) } fn is_weak(&self) -> bool { with_inner!(self.inner, SymbolInternal, |x| x.0.is_weak()) } fn scope(&self) -> SymbolScope { with_inner!(self.inner, SymbolInternal, |x| x.0.scope()) } fn is_global(&self) -> bool { with_inner!(self.inner, SymbolInternal, |x| x.0.is_global()) } fn is_local(&self) -> bool { with_inner!(self.inner, SymbolInternal, |x| x.0.is_local()) } fn flags(&self) -> SymbolFlags { with_inner!(self.inner, SymbolInternal, |x| x.0.flags()) } } /// An iterator for the dynamic relocation entries in a [`File`]. #[derive(Debug)] pub struct DynamicRelocationIterator<'data, 'file, R = &'data [u8]> where R: ReadRef<'data>, { inner: DynamicRelocationIteratorInternal<'data, 'file, R>, } #[derive(Debug)] enum DynamicRelocationIteratorInternal<'data, 'file, R> where R: ReadRef<'data>, { #[cfg(feature = "elf")] Elf32(elf::ElfDynamicRelocationIterator32<'data, 'file, Endianness, R>), #[cfg(feature = "elf")] Elf64(elf::ElfDynamicRelocationIterator64<'data, 'file, Endianness, R>), // We need to always use the lifetime parameters. #[allow(unused)] None(PhantomData<(&'data (), &'file (), R)>), } impl<'data, 'file, R: ReadRef<'data>> Iterator for DynamicRelocationIterator<'data, 'file, R> { type Item = (u64, Relocation); fn next(&mut self) -> Option { match self.inner { #[cfg(feature = "elf")] DynamicRelocationIteratorInternal::Elf32(ref mut elf) => elf.next(), #[cfg(feature = "elf")] DynamicRelocationIteratorInternal::Elf64(ref mut elf) => elf.next(), DynamicRelocationIteratorInternal::None(_) => None, } } } /// An iterator for the relocation entries in a [`Section`]. #[derive(Debug)] pub struct SectionRelocationIterator<'data, 'file, R: ReadRef<'data> = &'data [u8]> { inner: SectionRelocationIteratorInternal<'data, 'file, R>, } #[derive(Debug)] enum SectionRelocationIteratorInternal<'data, 'file, R: ReadRef<'data>> { #[cfg(feature = "coff")] Coff(coff::CoffRelocationIterator<'data, 'file, R>), #[cfg(feature = "coff")] CoffBig(coff::CoffBigRelocationIterator<'data, 'file, R>), #[cfg(feature = "elf")] Elf32(elf::ElfSectionRelocationIterator32<'data, 'file, Endianness, R>), #[cfg(feature = "elf")] Elf64(elf::ElfSectionRelocationIterator64<'data, 'file, Endianness, R>), #[cfg(feature = "macho")] MachO32(macho::MachORelocationIterator32<'data, 'file, Endianness, R>), #[cfg(feature = "macho")] MachO64(macho::MachORelocationIterator64<'data, 'file, Endianness, R>), #[cfg(feature = "pe")] Pe32(pe::PeRelocationIterator<'data, 'file, R>), #[cfg(feature = "pe")] Pe64(pe::PeRelocationIterator<'data, 'file, R>), #[cfg(feature = "wasm")] Wasm(wasm::WasmRelocationIterator<'data, 'file, R>), #[cfg(feature = "xcoff")] Xcoff32(xcoff::XcoffRelocationIterator32<'data, 'file, R>), #[cfg(feature = "xcoff")] Xcoff64(xcoff::XcoffRelocationIterator64<'data, 'file, R>), } impl<'data, 'file, R: ReadRef<'data>> Iterator for SectionRelocationIterator<'data, 'file, R> { type Item = (u64, Relocation); fn next(&mut self) -> Option { with_inner_mut!(self.inner, SectionRelocationIteratorInternal, |x| x.next()) } } object-0.36.5/src/read/archive.rs000064400000000000000000001215221046102023000146610ustar 00000000000000//! Support for archive files. //! //! ## Example //! ```no_run //! use object::{Object, ObjectSection}; //! use std::error::Error; //! use std::fs; //! //! /// Reads an archive and displays the name of each member. //! fn main() -> Result<(), Box> { //! # #[cfg(feature = "std")] { //! let data = fs::read("path/to/binary")?; //! let file = object::read::archive::ArchiveFile::parse(&*data)?; //! for member in file.members() { //! let member = member?; //! println!("{}", String::from_utf8_lossy(member.name())); //! } //! # } //! Ok(()) //! } //! ``` use core::convert::TryInto; use core::slice; use crate::archive; use crate::endian::{BigEndian as BE, LittleEndian as LE, U16Bytes, U32Bytes, U64Bytes}; use crate::read::{self, Bytes, Error, ReadError, ReadRef}; /// The kind of archive format. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum ArchiveKind { /// There are no special files that indicate the archive format. Unknown, /// The GNU (or System V) archive format. Gnu, /// The GNU (or System V) archive format with 64-bit symbol table. Gnu64, /// The BSD archive format. Bsd, /// The BSD archive format with 64-bit symbol table. /// /// This is used for Darwin. Bsd64, /// The Windows COFF archive format. Coff, /// The AIX big archive format. AixBig, } /// The list of members in the archive. #[derive(Debug, Clone, Copy)] enum Members<'data> { Common { offset: u64, end_offset: u64, }, AixBig { index: &'data [archive::AixMemberOffset], }, } /// A partially parsed archive file. #[derive(Debug, Clone, Copy)] pub struct ArchiveFile<'data, R: ReadRef<'data> = &'data [u8]> { data: R, kind: ArchiveKind, members: Members<'data>, symbols: (u64, u64), names: &'data [u8], thin: bool, } impl<'data, R: ReadRef<'data>> ArchiveFile<'data, R> { /// Parse the archive header and special members. pub fn parse(data: R) -> read::Result { let len = data.len().read_error("Unknown archive length")?; let mut tail = 0; let magic = data .read_bytes(&mut tail, archive::MAGIC.len() as u64) .read_error("Invalid archive size")?; let thin = if magic == archive::AIX_BIG_MAGIC { return Self::parse_aixbig(data); } else if magic == archive::THIN_MAGIC { true } else if magic == archive::MAGIC { false } else { return Err(Error("Unsupported archive identifier")); }; let mut members_offset = tail; let members_end_offset = len; let mut file = ArchiveFile { data, kind: ArchiveKind::Unknown, members: Members::Common { offset: 0, end_offset: 0, }, symbols: (0, 0), names: &[], thin, }; // The first few members may be special, so parse them. // GNU has: // - "/" or "/SYM64/": symbol table (optional) // - "//": names table (optional) // COFF has: // - "/": first linker member // - "/": second linker member // - "//": names table // BSD has: // - "__.SYMDEF" or "__.SYMDEF SORTED": symbol table (optional) // BSD 64-bit has: // - "__.SYMDEF_64" or "__.SYMDEF_64 SORTED": symbol table (optional) // BSD may use the extended name for the symbol table. This is handled // by `ArchiveMember::parse`. if tail < len { let member = ArchiveMember::parse(data, &mut tail, &[], thin)?; if member.name == b"/" { // GNU symbol table (unless we later determine this is COFF). file.kind = ArchiveKind::Gnu; file.symbols = member.file_range(); members_offset = tail; if tail < len { let member = ArchiveMember::parse(data, &mut tail, &[], thin)?; if member.name == b"/" { // COFF linker member. file.kind = ArchiveKind::Coff; file.symbols = member.file_range(); members_offset = tail; if tail < len { let member = ArchiveMember::parse(data, &mut tail, &[], thin)?; if member.name == b"//" { // COFF names table. file.names = member.data(data)?; members_offset = tail; } } if tail < len { let member = ArchiveMember::parse(data, &mut tail, file.names, thin)?; if member.name == b"//" { // COFF EC Symbol Table. members_offset = tail; } } } else if member.name == b"//" { // GNU names table. file.names = member.data(data)?; members_offset = tail; } } } else if member.name == b"/SYM64/" { // GNU 64-bit symbol table. file.kind = ArchiveKind::Gnu64; file.symbols = member.file_range(); members_offset = tail; if tail < len { let member = ArchiveMember::parse(data, &mut tail, &[], thin)?; if member.name == b"//" { // GNU names table. file.names = member.data(data)?; members_offset = tail; } } } else if member.name == b"//" { // GNU names table. file.kind = ArchiveKind::Gnu; file.names = member.data(data)?; members_offset = tail; } else if member.name == b"__.SYMDEF" || member.name == b"__.SYMDEF SORTED" { // BSD symbol table. file.kind = ArchiveKind::Bsd; file.symbols = member.file_range(); members_offset = tail; } else if member.name == b"__.SYMDEF_64" || member.name == b"__.SYMDEF_64 SORTED" { // BSD 64-bit symbol table. file.kind = ArchiveKind::Bsd64; file.symbols = member.file_range(); members_offset = tail; } else { // TODO: This could still be a BSD file. We leave this as unknown for now. } } file.members = Members::Common { offset: members_offset, end_offset: members_end_offset, }; Ok(file) } fn parse_aixbig(data: R) -> read::Result { let mut tail = 0; let file_header = data .read::(&mut tail) .read_error("Invalid AIX big archive file header")?; // Caller already validated this. debug_assert_eq!(file_header.magic, archive::AIX_BIG_MAGIC); let mut file = ArchiveFile { data, kind: ArchiveKind::AixBig, members: Members::AixBig { index: &[] }, symbols: (0, 0), names: &[], thin: false, }; // Read the span of symbol table. // TODO: an archive may have both 32-bit and 64-bit symbol tables. let symtbl64 = parse_u64_digits(&file_header.gst64off, 10) .read_error("Invalid offset to 64-bit symbol table in AIX big archive")?; if symtbl64 > 0 { // The symbol table is also a file with header. let member = ArchiveMember::parse_aixbig(data, symtbl64)?; file.symbols = member.file_range(); } else { let symtbl = parse_u64_digits(&file_header.gstoff, 10) .read_error("Invalid offset to symbol table in AIX big archive")?; if symtbl > 0 { // The symbol table is also a file with header. let member = ArchiveMember::parse_aixbig(data, symtbl)?; file.symbols = member.file_range(); } } // Big archive member index table lists file entries with offsets and names. // To avoid potential infinite loop (members are double-linked list), the // iterator goes through the index instead of real members. let member_table_offset = parse_u64_digits(&file_header.memoff, 10) .read_error("Invalid offset for member table of AIX big archive")?; if member_table_offset == 0 { // The offset would be zero if archive contains no file. return Ok(file); } // The member index table is also a file with header. let member = ArchiveMember::parse_aixbig(data, member_table_offset)?; let mut member_data = Bytes(member.data(data)?); // Structure of member index table: // Number of entries (20 bytes) // Offsets of each entry (20*N bytes) // Names string table (the rest of bytes to fill size defined in header) let members_count_bytes = member_data .read_slice::(20) .read_error("Missing member count in AIX big archive")?; let members_count = parse_u64_digits(members_count_bytes, 10) .and_then(|size| size.try_into().ok()) .read_error("Invalid member count in AIX big archive")?; let index = member_data .read_slice::(members_count) .read_error("Member count overflow in AIX big archive")?; file.members = Members::AixBig { index }; Ok(file) } /// Return the archive format. #[inline] pub fn kind(&self) -> ArchiveKind { self.kind } /// Return true if the archive is a thin archive. pub fn is_thin(&self) -> bool { self.thin } /// Iterate over the members of the archive. /// /// This does not return special members. #[inline] pub fn members(&self) -> ArchiveMemberIterator<'data, R> { ArchiveMemberIterator { data: self.data, members: self.members, names: self.names, thin: self.thin, } } /// Return the member at the given offset. pub fn member(&self, member: ArchiveOffset) -> read::Result> { match self.members { Members::Common { offset, end_offset } => { if member.0 < offset || member.0 >= end_offset { return Err(Error("Invalid archive member offset")); } let mut offset = member.0; ArchiveMember::parse(self.data, &mut offset, self.names, self.thin) } Members::AixBig { .. } => { let offset = member.0; ArchiveMember::parse_aixbig(self.data, offset) } } } /// Iterate over the symbols in the archive. pub fn symbols(&self) -> read::Result>> { if self.symbols == (0, 0) { return Ok(None); } let (offset, size) = self.symbols; ArchiveSymbolIterator::new(self.kind, self.data, offset, size) .read_error("Invalid archive symbol table") .map(Some) } } /// An iterator over the members of an archive. #[derive(Debug)] pub struct ArchiveMemberIterator<'data, R: ReadRef<'data> = &'data [u8]> { data: R, members: Members<'data>, names: &'data [u8], thin: bool, } impl<'data, R: ReadRef<'data>> Iterator for ArchiveMemberIterator<'data, R> { type Item = read::Result>; fn next(&mut self) -> Option { match &mut self.members { Members::Common { ref mut offset, ref mut end_offset, } => { if *offset >= *end_offset { return None; } let member = ArchiveMember::parse(self.data, offset, self.names, self.thin); if member.is_err() { *offset = *end_offset; } Some(member) } Members::AixBig { ref mut index } => match **index { [] => None, [ref first, ref rest @ ..] => { *index = rest; let member = ArchiveMember::parse_aixbig_index(self.data, first); if member.is_err() { *index = &[]; } Some(member) } }, } } } /// An archive member header. #[derive(Debug, Clone, Copy)] enum MemberHeader<'data> { /// Common header used by many formats. Common(&'data archive::Header), /// AIX big archive header AixBig(&'data archive::AixHeader), } /// A partially parsed archive member. #[derive(Debug)] pub struct ArchiveMember<'data> { header: MemberHeader<'data>, name: &'data [u8], // May be zero for thin members. offset: u64, size: u64, } impl<'data> ArchiveMember<'data> { /// Parse the member header, name, and file data in an archive with the common format. /// /// This reads the extended name (if any) and adjusts the file size. fn parse>( data: R, offset: &mut u64, names: &'data [u8], thin: bool, ) -> read::Result { let header = data .read::(offset) .read_error("Invalid archive member header")?; if header.terminator != archive::TERMINATOR { return Err(Error("Invalid archive terminator")); } let header_file_size = parse_u64_digits(&header.size, 10).read_error("Invalid archive member size")?; let mut file_offset = *offset; let mut file_size = header_file_size; let name = if header.name[0] == b'/' && (header.name[1] as char).is_ascii_digit() { // Read file name from the names table. parse_sysv_extended_name(&header.name[1..], names) .read_error("Invalid archive extended name offset")? } else if &header.name[..3] == b"#1/" && (header.name[3] as char).is_ascii_digit() { // Read file name from the start of the file data. parse_bsd_extended_name(&header.name[3..], data, &mut file_offset, &mut file_size) .read_error("Invalid archive extended name length")? } else if header.name[0] == b'/' { let name_len = memchr::memchr(b' ', &header.name).unwrap_or(header.name.len()); &header.name[..name_len] } else { // Name is terminated by slash or space. // Slash allows embedding spaces in the name, so only look // for space if there is no slash. let name_len = memchr::memchr(b'/', &header.name) .or_else(|| memchr::memchr(b' ', &header.name)) .unwrap_or(header.name.len()); &header.name[..name_len] }; // Members in thin archives don't have data unless they are special members. if thin && name != b"/" && name != b"//" && name != b"/SYM64/" { return Ok(ArchiveMember { header: MemberHeader::Common(header), name, offset: 0, size: file_size, }); } // Skip the file data. *offset = offset .checked_add(header_file_size) .read_error("Archive member size is too large")?; // Entries are padded to an even number of bytes. if (header_file_size & 1) != 0 { *offset = offset.saturating_add(1); } Ok(ArchiveMember { header: MemberHeader::Common(header), name, offset: file_offset, size: file_size, }) } /// Parse a member index entry in an AIX big archive, /// and then parse the member header, name, and file data. fn parse_aixbig_index>( data: R, index: &archive::AixMemberOffset, ) -> read::Result { let offset = parse_u64_digits(&index.0, 10) .read_error("Invalid AIX big archive file member offset")?; Self::parse_aixbig(data, offset) } /// Parse the member header, name, and file data in an AIX big archive. fn parse_aixbig>(data: R, mut offset: u64) -> read::Result { // The format was described at // https://www.ibm.com/docs/en/aix/7.3?topic=formats-ar-file-format-big let header = data .read::(&mut offset) .read_error("Invalid AIX big archive member header")?; let name_length = parse_u64_digits(&header.namlen, 10) .read_error("Invalid AIX big archive member name length")?; let name = data .read_bytes(&mut offset, name_length) .read_error("Invalid AIX big archive member name")?; // The actual data for a file member begins at the first even-byte boundary beyond the // member header and continues for the number of bytes specified by the ar_size field. The // ar command inserts null bytes for padding where necessary. if offset & 1 != 0 { offset = offset.saturating_add(1); } // Because of the even-byte boundary, we have to read and check terminator after header. let terminator = data .read_bytes(&mut offset, 2) .read_error("Invalid AIX big archive terminator")?; if terminator != archive::TERMINATOR { return Err(Error("Invalid AIX big archive terminator")); } let size = parse_u64_digits(&header.size, 10) .read_error("Invalid archive member size in AIX big archive")?; Ok(ArchiveMember { header: MemberHeader::AixBig(header), name, offset, size, }) } /// Return the raw header that is common to many archive formats. /// /// Returns `None` if this archive does not use the common header format. #[inline] pub fn header(&self) -> Option<&'data archive::Header> { match self.header { MemberHeader::Common(header) => Some(header), _ => None, } } /// Return the raw header for AIX big archives. /// /// Returns `None` if this is not an AIX big archive. #[inline] pub fn aix_header(&self) -> Option<&'data archive::AixHeader> { match self.header { MemberHeader::AixBig(header) => Some(header), _ => None, } } /// Return the parsed file name. /// /// This may be an extended file name. #[inline] pub fn name(&self) -> &'data [u8] { self.name } /// Parse the file modification timestamp from the header. #[inline] pub fn date(&self) -> Option { match &self.header { MemberHeader::Common(header) => parse_u64_digits(&header.date, 10), MemberHeader::AixBig(header) => parse_u64_digits(&header.date, 10), } } /// Parse the user ID from the header. #[inline] pub fn uid(&self) -> Option { match &self.header { MemberHeader::Common(header) => parse_u64_digits(&header.uid, 10), MemberHeader::AixBig(header) => parse_u64_digits(&header.uid, 10), } } /// Parse the group ID from the header. #[inline] pub fn gid(&self) -> Option { match &self.header { MemberHeader::Common(header) => parse_u64_digits(&header.gid, 10), MemberHeader::AixBig(header) => parse_u64_digits(&header.gid, 10), } } /// Parse the file mode from the header. #[inline] pub fn mode(&self) -> Option { match &self.header { MemberHeader::Common(header) => parse_u64_digits(&header.mode, 8), MemberHeader::AixBig(header) => parse_u64_digits(&header.mode, 8), } } /// Return the size of the file data. pub fn size(&self) -> u64 { self.size } /// Return the offset and size of the file data. pub fn file_range(&self) -> (u64, u64) { (self.offset, self.size) } /// Return true if the member is a thin member. /// /// Thin members have no file data. pub fn is_thin(&self) -> bool { self.offset == 0 } /// Return the file data. /// /// This is an empty slice for thin members. #[inline] pub fn data>(&self, data: R) -> read::Result<&'data [u8]> { if self.is_thin() { return Ok(&[]); } data.read_bytes_at(self.offset, self.size) .read_error("Archive member size is too large") } } /// An offset of a member in an archive. #[derive(Debug, Clone, Copy)] pub struct ArchiveOffset(pub u64); /// An iterator over the symbols in the archive symbol table. #[derive(Debug, Clone)] pub struct ArchiveSymbolIterator<'data>(SymbolIteratorInternal<'data>); #[derive(Debug, Clone)] enum SymbolIteratorInternal<'data> { /// There is no symbol table. None, /// A GNU symbol table. /// /// Contains: /// - the number of symbols as a 32-bit big-endian integer /// - the offsets of the member headers as 32-bit big-endian integers /// - the symbol names as null-terminated strings Gnu { offsets: slice::Iter<'data, U32Bytes>, names: Bytes<'data>, }, /// A GNU 64-bit symbol table /// /// Contains: /// - the number of symbols as a 64-bit big-endian integer /// - the offsets of the member headers as 64-bit big-endian integers /// - the symbol names as null-terminated strings Gnu64 { offsets: slice::Iter<'data, U64Bytes>, names: Bytes<'data>, }, /// A BSD symbol table. /// /// Contains: /// - the size in bytes of the offsets array as a 32-bit little-endian integer /// - the offsets array, for which each entry is a pair of 32-bit little-endian integers /// for the offset of the member header and the offset of the symbol name /// - the size in bytes of the symbol names as a 32-bit little-endian integer /// - the symbol names as null-terminated strings Bsd { offsets: slice::Iter<'data, [U32Bytes; 2]>, names: Bytes<'data>, }, /// A BSD 64-bit symbol table. /// /// Contains: /// - the size in bytes of the offsets array as a 64-bit little-endian integer /// - the offsets array, for which each entry is a pair of 64-bit little-endian integers /// for the offset of the member header and the offset of the symbol name /// - the size in bytes of the symbol names as a 64-bit little-endian integer /// - the symbol names as null-terminated strings Bsd64 { offsets: slice::Iter<'data, [U64Bytes; 2]>, names: Bytes<'data>, }, /// A Windows COFF symbol table. /// /// Contains: /// - the number of members as a 32-bit little-endian integer /// - the offsets of the member headers as 32-bit little-endian integers /// - the number of symbols as a 32-bit little-endian integer /// - the member index for each symbol as a 16-bit little-endian integer /// - the symbol names as null-terminated strings in lexical order Coff { members: &'data [U32Bytes], indices: slice::Iter<'data, U16Bytes>, names: Bytes<'data>, }, } impl<'data> ArchiveSymbolIterator<'data> { fn new>( kind: ArchiveKind, data: R, offset: u64, size: u64, ) -> Result { let mut data = data.read_bytes_at(offset, size).map(Bytes)?; match kind { ArchiveKind::Unknown => Ok(ArchiveSymbolIterator(SymbolIteratorInternal::None)), ArchiveKind::Gnu => { let offsets_count = data.read::>()?.get(BE); let offsets = data.read_slice::>(offsets_count as usize)?; Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Gnu { offsets: offsets.iter(), names: data, })) } ArchiveKind::Gnu64 => { let offsets_count = data.read::>()?.get(BE); let offsets = data.read_slice::>(offsets_count as usize)?; Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Gnu64 { offsets: offsets.iter(), names: data, })) } ArchiveKind::Bsd => { let offsets_size = data.read::>()?.get(LE); let offsets = data.read_slice::<[U32Bytes; 2]>(offsets_size as usize / 8)?; let names_size = data.read::>()?.get(LE); let names = data.read_bytes(names_size as usize)?; Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Bsd { offsets: offsets.iter(), names, })) } ArchiveKind::Bsd64 => { let offsets_size = data.read::>()?.get(LE); let offsets = data.read_slice::<[U64Bytes; 2]>(offsets_size as usize / 16)?; let names_size = data.read::>()?.get(LE); let names = data.read_bytes(names_size as usize)?; Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Bsd64 { offsets: offsets.iter(), names, })) } ArchiveKind::Coff => { let members_count = data.read::>()?.get(LE); let members = data.read_slice::>(members_count as usize)?; let indices_count = data.read::>()?.get(LE); let indices = data.read_slice::>(indices_count as usize)?; Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Coff { members, indices: indices.iter(), names: data, })) } // TODO: Implement AIX big archive symbol table. ArchiveKind::AixBig => Ok(ArchiveSymbolIterator(SymbolIteratorInternal::None)), } } } impl<'data> Iterator for ArchiveSymbolIterator<'data> { type Item = read::Result>; fn next(&mut self) -> Option { match &mut self.0 { SymbolIteratorInternal::None => None, SymbolIteratorInternal::Gnu { offsets, names } => { let offset = offsets.next()?.get(BE); Some( names .read_string() .read_error("Missing archive symbol name") .map(|name| ArchiveSymbol { name, offset: ArchiveOffset(offset.into()), }), ) } SymbolIteratorInternal::Gnu64 { offsets, names } => { let offset = offsets.next()?.get(BE); Some( names .read_string() .read_error("Missing archive symbol name") .map(|name| ArchiveSymbol { name, offset: ArchiveOffset(offset), }), ) } SymbolIteratorInternal::Bsd { offsets, names } => { let entry = offsets.next()?; Some( names .read_string_at(entry[0].get(LE) as usize) .read_error("Invalid archive symbol name offset") .map(|name| ArchiveSymbol { name, offset: ArchiveOffset(entry[1].get(LE).into()), }), ) } SymbolIteratorInternal::Bsd64 { offsets, names } => { let entry = offsets.next()?; Some( names .read_string_at(entry[0].get(LE) as usize) .read_error("Invalid archive symbol name offset") .map(|name| ArchiveSymbol { name, offset: ArchiveOffset(entry[1].get(LE)), }), ) } SymbolIteratorInternal::Coff { members, indices, names, } => { let index = indices.next()?.get(LE).wrapping_sub(1); let member = members .get(index as usize) .read_error("Invalid archive symbol member index"); let name = names .read_string() .read_error("Missing archive symbol name"); Some(member.and_then(|member| { name.map(|name| ArchiveSymbol { name, offset: ArchiveOffset(member.get(LE).into()), }) })) } } } } /// A symbol in the archive symbol table. /// /// This is used to find the member containing the symbol. #[derive(Debug, Clone, Copy)] pub struct ArchiveSymbol<'data> { name: &'data [u8], offset: ArchiveOffset, } impl<'data> ArchiveSymbol<'data> { /// Return the symbol name. #[inline] pub fn name(&self) -> &'data [u8] { self.name } /// Return the offset of the header for the member containing the symbol. #[inline] pub fn offset(&self) -> ArchiveOffset { self.offset } } // Ignores bytes starting from the first space. fn parse_u64_digits(digits: &[u8], radix: u32) -> Option { if let [b' ', ..] = digits { return None; } let mut result: u64 = 0; for &c in digits { if c == b' ' { return Some(result); } else { let x = (c as char).to_digit(radix)?; result = result .checked_mul(u64::from(radix))? .checked_add(u64::from(x))?; } } Some(result) } /// Digits are a decimal offset into the extended name table. /// Name is terminated by "/\n" (for GNU) or a null byte (for COFF). fn parse_sysv_extended_name<'data>(digits: &[u8], names: &'data [u8]) -> Result<&'data [u8], ()> { let offset = parse_u64_digits(digits, 10).ok_or(())?; let offset = offset.try_into().map_err(|_| ())?; let name_data = names.get(offset..).ok_or(())?; let len = memchr::memchr2(b'\n', b'\0', name_data).ok_or(())?; if name_data[len] == b'\n' { if len < 1 || name_data[len - 1] != b'/' { Err(()) } else { Ok(&name_data[..len - 1]) } } else { Ok(&name_data[..len]) } } /// Digits are a decimal length of the extended name, which is contained /// in `data` at `offset`. /// Modifies `offset` and `size` to start after the extended name. fn parse_bsd_extended_name<'data, R: ReadRef<'data>>( digits: &[u8], data: R, offset: &mut u64, size: &mut u64, ) -> Result<&'data [u8], ()> { let len = parse_u64_digits(digits, 10).ok_or(())?; *size = size.checked_sub(len).ok_or(())?; let name_data = data.read_bytes(offset, len)?; let name = match memchr::memchr(b'\0', name_data) { Some(len) => &name_data[..len], None => name_data, }; Ok(name) } #[cfg(test)] mod tests { use super::*; #[test] fn kind() { let data = b"!\n"; let archive = ArchiveFile::parse(&data[..]).unwrap(); assert_eq!(archive.kind(), ArchiveKind::Unknown); let data = b"\ !\n\ / 4 `\n\ 0000"; let archive = ArchiveFile::parse(&data[..]).unwrap(); assert_eq!(archive.kind(), ArchiveKind::Gnu); let data = b"\ !\n\ // 4 `\n\ 0000"; let archive = ArchiveFile::parse(&data[..]).unwrap(); assert_eq!(archive.kind(), ArchiveKind::Gnu); let data = b"\ !\n\ / 4 `\n\ 0000\ // 4 `\n\ 0000"; let archive = ArchiveFile::parse(&data[..]).unwrap(); assert_eq!(archive.kind(), ArchiveKind::Gnu); let data = b"\ !\n\ /SYM64/ 4 `\n\ 0000"; let archive = ArchiveFile::parse(&data[..]).unwrap(); assert_eq!(archive.kind(), ArchiveKind::Gnu64); let data = b"\ !\n\ /SYM64/ 4 `\n\ 0000\ // 4 `\n\ 0000"; let archive = ArchiveFile::parse(&data[..]).unwrap(); assert_eq!(archive.kind(), ArchiveKind::Gnu64); let data = b"\ !\n\ __.SYMDEF 4 `\n\ 0000"; let archive = ArchiveFile::parse(&data[..]).unwrap(); assert_eq!(archive.kind(), ArchiveKind::Bsd); let data = b"\ !\n\ #1/9 13 `\n\ __.SYMDEF0000"; let archive = ArchiveFile::parse(&data[..]).unwrap(); assert_eq!(archive.kind(), ArchiveKind::Bsd); let data = b"\ !\n\ #1/16 20 `\n\ __.SYMDEF SORTED0000"; let archive = ArchiveFile::parse(&data[..]).unwrap(); assert_eq!(archive.kind(), ArchiveKind::Bsd); let data = b"\ !\n\ __.SYMDEF_64 4 `\n\ 0000"; let archive = ArchiveFile::parse(&data[..]).unwrap(); assert_eq!(archive.kind(), ArchiveKind::Bsd64); let data = b"\ !\n\ #1/12 16 `\n\ __.SYMDEF_640000"; let archive = ArchiveFile::parse(&data[..]).unwrap(); assert_eq!(archive.kind(), ArchiveKind::Bsd64); let data = b"\ !\n\ #1/19 23 `\n\ __.SYMDEF_64 SORTED0000"; let archive = ArchiveFile::parse(&data[..]).unwrap(); assert_eq!(archive.kind(), ArchiveKind::Bsd64); let data = b"\ !\n\ / 4 `\n\ 0000\ / 4 `\n\ 0000\ // 4 `\n\ 0000"; let archive = ArchiveFile::parse(&data[..]).unwrap(); assert_eq!(archive.kind(), ArchiveKind::Coff); let data = b"\ \n\ 0 0 \ 0 0 \ 0 128 \ 6 0 \ 0 \0\0\0\0\0\0\0\0\0\0\0\0\ \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; let archive = ArchiveFile::parse(&data[..]).unwrap(); assert_eq!(archive.kind(), ArchiveKind::AixBig); let data = b"\ !\n\ / 4 `\n\ 0000"; let archive = ArchiveFile::parse(&data[..]).unwrap(); assert_eq!(archive.kind(), ArchiveKind::Gnu); assert!(archive.is_thin()); } #[test] fn gnu_names() { let data = b"\ !\n\ // 18 `\n\ 0123456789abcdef/\n\ s p a c e/ 0 0 0 644 4 `\n\ 0000\ 0123456789abcde/0 0 0 644 3 `\n\ odd\n\ /0 0 0 0 644 4 `\n\ even"; let data = &data[..]; let archive = ArchiveFile::parse(data).unwrap(); assert_eq!(archive.kind(), ArchiveKind::Gnu); let mut members = archive.members(); let member = members.next().unwrap().unwrap(); assert_eq!(member.name(), b"s p a c e"); assert_eq!(member.data(data).unwrap(), &b"0000"[..]); let member = members.next().unwrap().unwrap(); assert_eq!(member.name(), b"0123456789abcde"); assert_eq!(member.data(data).unwrap(), &b"odd"[..]); let member = members.next().unwrap().unwrap(); assert_eq!(member.name(), b"0123456789abcdef"); assert_eq!(member.data(data).unwrap(), &b"even"[..]); assert!(members.next().is_none()); } #[test] fn thin_gnu_names() { let data = b"\ !\n\ // 18 `\n\ 0123456789/abcde/\n\ s p a c e/ 0 0 0 644 4 `\n\ 0123456789abcde/0 0 0 644 3 `\n\ /0 0 0 0 644 4 `\n\ "; let data = &data[..]; let archive = ArchiveFile::parse(data).unwrap(); assert_eq!(archive.kind(), ArchiveKind::Gnu); let mut members = archive.members(); let member = members.next().unwrap().unwrap(); assert_eq!(member.name(), b"s p a c e"); assert!(member.is_thin()); assert_eq!(member.size(), 4); assert_eq!(member.data(data).unwrap(), &[]); let member = members.next().unwrap().unwrap(); assert_eq!(member.name(), b"0123456789abcde"); assert!(member.is_thin()); assert_eq!(member.size(), 3); assert_eq!(member.data(data).unwrap(), &[]); let member = members.next().unwrap().unwrap(); assert_eq!(member.name(), b"0123456789/abcde"); assert!(member.is_thin()); assert_eq!(member.size(), 4); assert_eq!(member.data(data).unwrap(), &[]); assert!(members.next().is_none()); } #[test] fn bsd_names() { let data = b"\ !\n\ 0123456789abcde 0 0 0 644 3 `\n\ odd\n\ #1/16 0 0 0 644 20 `\n\ 0123456789abcdefeven"; let data = &data[..]; let archive = ArchiveFile::parse(data).unwrap(); assert_eq!(archive.kind(), ArchiveKind::Unknown); let mut members = archive.members(); let member = members.next().unwrap().unwrap(); assert_eq!(member.name(), b"0123456789abcde"); assert_eq!(member.data(data).unwrap(), &b"odd"[..]); let member = members.next().unwrap().unwrap(); assert_eq!(member.name(), b"0123456789abcdef"); assert_eq!(member.data(data).unwrap(), &b"even"[..]); assert!(members.next().is_none()); } #[test] fn aix_names() { let data = b"\ \n\ 396 0 0 \ 128 262 0 \ 4 262 0 \ 1662610370 223 1 644 16 \ 0123456789abcdef`\nord\n\ 4 396 128 \ 1662610374 223 1 644 16 \ fedcba9876543210`\nrev\n\ 94 0 262 \ 0 0 0 0 0 \ `\n2 128 \ 262 0123456789abcdef\0fedcba9876543210\0"; let data = &data[..]; let archive = ArchiveFile::parse(data).unwrap(); assert_eq!(archive.kind(), ArchiveKind::AixBig); let mut members = archive.members(); let member = members.next().unwrap().unwrap(); assert_eq!(member.name(), b"0123456789abcdef"); assert_eq!(member.data(data).unwrap(), &b"ord\n"[..]); let member = members.next().unwrap().unwrap(); assert_eq!(member.name(), b"fedcba9876543210"); assert_eq!(member.data(data).unwrap(), &b"rev\n"[..]); assert!(members.next().is_none()); } } object-0.36.5/src/read/coff/comdat.rs000064400000000000000000000160061046102023000154240ustar 00000000000000use core::str; use crate::endian::LittleEndian as LE; use crate::pe; use crate::read::{ self, ComdatKind, ObjectComdat, ReadError, ReadRef, Result, SectionIndex, SymbolIndex, }; use super::{CoffFile, CoffHeader, ImageSymbol}; /// An iterator for the COMDAT section groups in a [`CoffBigFile`](super::CoffBigFile). pub type CoffBigComdatIterator<'data, 'file, R = &'data [u8]> = CoffComdatIterator<'data, 'file, R, pe::AnonObjectHeaderBigobj>; /// An iterator for the COMDAT section groups in a [`CoffFile`]. #[derive(Debug)] pub struct CoffComdatIterator< 'data, 'file, R: ReadRef<'data> = &'data [u8], Coff: CoffHeader = pe::ImageFileHeader, > { file: &'file CoffFile<'data, R, Coff>, index: SymbolIndex, } impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> CoffComdatIterator<'data, 'file, R, Coff> { pub(crate) fn new(file: &'file CoffFile<'data, R, Coff>) -> Self { CoffComdatIterator { file, index: SymbolIndex(0), } } } impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> Iterator for CoffComdatIterator<'data, 'file, R, Coff> { type Item = CoffComdat<'data, 'file, R, Coff>; fn next(&mut self) -> Option { loop { let index = self.index; let symbol = self.file.common.symbols.symbol(index).ok()?; self.index.0 += 1 + symbol.number_of_aux_symbols() as usize; if let Some(comdat) = CoffComdat::parse(self.file, symbol, index) { return Some(comdat); } } } } /// A COMDAT section group in a [`CoffBigFile`](super::CoffBigFile). /// /// Most functionality is provided by the [`ObjectComdat`] trait implementation. pub type CoffBigComdat<'data, 'file, R = &'data [u8]> = CoffComdat<'data, 'file, R, pe::AnonObjectHeaderBigobj>; /// A COMDAT section group in a [`CoffFile`]. /// /// Most functionality is provided by the [`ObjectComdat`] trait implementation. #[derive(Debug)] pub struct CoffComdat< 'data, 'file, R: ReadRef<'data> = &'data [u8], Coff: CoffHeader = pe::ImageFileHeader, > { file: &'file CoffFile<'data, R, Coff>, symbol_index: SymbolIndex, symbol: &'data Coff::ImageSymbol, selection: u8, } impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> CoffComdat<'data, 'file, R, Coff> { fn parse( file: &'file CoffFile<'data, R, Coff>, section_symbol: &'data Coff::ImageSymbol, index: SymbolIndex, ) -> Option> { // Must be a section symbol. if !section_symbol.has_aux_section() { return None; } // Auxiliary record must have a non-associative selection. let aux = file.common.symbols.aux_section(index).ok()?; let selection = aux.selection; if selection == 0 || selection == pe::IMAGE_COMDAT_SELECT_ASSOCIATIVE { return None; } // Find the COMDAT symbol. let mut symbol_index = index; let mut symbol = section_symbol; let section_number = section_symbol.section_number(); loop { symbol_index.0 += 1 + symbol.number_of_aux_symbols() as usize; symbol = file.common.symbols.symbol(symbol_index).ok()?; if section_number == symbol.section_number() { break; } } Some(CoffComdat { file, symbol_index, symbol, selection, }) } } impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> read::private::Sealed for CoffComdat<'data, 'file, R, Coff> { } impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> ObjectComdat<'data> for CoffComdat<'data, 'file, R, Coff> { type SectionIterator = CoffComdatSectionIterator<'data, 'file, R, Coff>; #[inline] fn kind(&self) -> ComdatKind { match self.selection { pe::IMAGE_COMDAT_SELECT_NODUPLICATES => ComdatKind::NoDuplicates, pe::IMAGE_COMDAT_SELECT_ANY => ComdatKind::Any, pe::IMAGE_COMDAT_SELECT_SAME_SIZE => ComdatKind::SameSize, pe::IMAGE_COMDAT_SELECT_EXACT_MATCH => ComdatKind::ExactMatch, pe::IMAGE_COMDAT_SELECT_LARGEST => ComdatKind::Largest, pe::IMAGE_COMDAT_SELECT_NEWEST => ComdatKind::Newest, _ => ComdatKind::Unknown, } } #[inline] fn symbol(&self) -> SymbolIndex { self.symbol_index } #[inline] fn name_bytes(&self) -> Result<&'data [u8]> { // Find the name of first symbol referring to the section. self.symbol.name(self.file.common.symbols.strings()) } #[inline] fn name(&self) -> Result<&'data str> { let bytes = self.name_bytes()?; str::from_utf8(bytes) .ok() .read_error("Non UTF-8 COFF COMDAT name") } #[inline] fn sections(&self) -> Self::SectionIterator { CoffComdatSectionIterator { file: self.file, section_number: self.symbol.section_number(), index: SymbolIndex(0), } } } /// An iterator for the sections in a COMDAT section group in a [`CoffBigFile`](super::CoffBigFile). pub type CoffBigComdatSectionIterator<'data, 'file, R = &'data [u8]> = CoffComdatSectionIterator<'data, 'file, R, pe::AnonObjectHeaderBigobj>; /// An iterator for the sections in a COMDAT section group in a [`CoffFile`]. #[derive(Debug)] pub struct CoffComdatSectionIterator< 'data, 'file, R: ReadRef<'data> = &'data [u8], Coff: CoffHeader = pe::ImageFileHeader, > { file: &'file CoffFile<'data, R, Coff>, section_number: i32, index: SymbolIndex, } impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> Iterator for CoffComdatSectionIterator<'data, 'file, R, Coff> { type Item = SectionIndex; fn next(&mut self) -> Option { // Find associated COMDAT symbols. // TODO: it seems gcc doesn't use associated symbols for this loop { let index = self.index; let symbol = self.file.common.symbols.symbol(index).ok()?; self.index.0 += 1 + symbol.number_of_aux_symbols() as usize; // Must be a section symbol. if !symbol.has_aux_section() { continue; } let section_number = symbol.section_number(); let aux = self.file.common.symbols.aux_section(index).ok()?; if aux.selection == pe::IMAGE_COMDAT_SELECT_ASSOCIATIVE { let number = if Coff::is_type_bigobj() { u32::from(aux.number.get(LE)) | (u32::from(aux.high_number.get(LE)) << 16) } else { u32::from(aux.number.get(LE)) }; if number as i32 == self.section_number { return Some(SectionIndex(section_number as usize)); } } else if aux.selection != 0 { if section_number == self.section_number { return Some(SectionIndex(section_number as usize)); } } } } } object-0.36.5/src/read/coff/file.rs000064400000000000000000000267721046102023000151070ustar 00000000000000use alloc::vec::Vec; use core::fmt::Debug; use crate::endian::LittleEndian as LE; use crate::pe; use crate::pod::Pod; use crate::read::{ self, Architecture, Export, FileFlags, Import, NoDynamicRelocationIterator, Object, ObjectKind, ObjectSection, ReadError, ReadRef, Result, SectionIndex, SubArchitecture, SymbolIndex, }; use super::{ CoffComdat, CoffComdatIterator, CoffSection, CoffSectionIterator, CoffSegment, CoffSegmentIterator, CoffSymbol, CoffSymbolIterator, CoffSymbolTable, ImageSymbol, SectionTable, SymbolTable, }; /// The common parts of `PeFile` and `CoffFile`. #[derive(Debug)] pub(crate) struct CoffCommon<'data, R: ReadRef<'data>, Coff: CoffHeader = pe::ImageFileHeader> { pub(crate) sections: SectionTable<'data>, pub(crate) symbols: SymbolTable<'data, R, Coff>, pub(crate) image_base: u64, } /// A COFF bigobj object file with 32-bit section numbers. /// /// This is a file that starts with [`pe::AnonObjectHeaderBigobj`], and corresponds /// to [`crate::FileKind::CoffBig`]. /// /// Most functionality is provided by the [`Object`] trait implementation. pub type CoffBigFile<'data, R = &'data [u8]> = CoffFile<'data, R, pe::AnonObjectHeaderBigobj>; /// A COFF object file. /// /// This is a file that starts with [`pe::ImageFileHeader`], and corresponds /// to [`crate::FileKind::Coff`]. /// /// Most functionality is provided by the [`Object`] trait implementation. #[derive(Debug)] pub struct CoffFile<'data, R: ReadRef<'data> = &'data [u8], Coff: CoffHeader = pe::ImageFileHeader> { pub(super) header: &'data Coff, pub(super) common: CoffCommon<'data, R, Coff>, pub(super) data: R, } impl<'data, R: ReadRef<'data>, Coff: CoffHeader> CoffFile<'data, R, Coff> { /// Parse the raw COFF file data. pub fn parse(data: R) -> Result { let mut offset = 0; let header = Coff::parse(data, &mut offset)?; let sections = header.sections(data, offset)?; let symbols = header.symbols(data)?; Ok(CoffFile { header, common: CoffCommon { sections, symbols, image_base: 0, }, data, }) } /// Get the raw COFF file header. pub fn coff_header(&self) -> &'data Coff { self.header } /// Get the COFF section table. pub fn coff_section_table(&self) -> SectionTable<'data> { self.common.sections } /// Get the COFF symbol table. pub fn coff_symbol_table(&self) -> &SymbolTable<'data, R, Coff> { &self.common.symbols } } impl<'data, R: ReadRef<'data>, Coff: CoffHeader> read::private::Sealed for CoffFile<'data, R, Coff> { } impl<'data, R, Coff> Object<'data> for CoffFile<'data, R, Coff> where R: ReadRef<'data>, Coff: CoffHeader, { type Segment<'file> = CoffSegment<'data, 'file, R, Coff> where Self: 'file, 'data: 'file; type SegmentIterator<'file> = CoffSegmentIterator<'data, 'file, R, Coff> where Self: 'file, 'data: 'file; type Section<'file> = CoffSection<'data, 'file, R, Coff> where Self: 'file, 'data: 'file; type SectionIterator<'file> = CoffSectionIterator<'data, 'file, R, Coff> where Self: 'file, 'data: 'file; type Comdat<'file> = CoffComdat<'data, 'file, R, Coff> where Self: 'file, 'data: 'file; type ComdatIterator<'file> = CoffComdatIterator<'data, 'file, R, Coff> where Self: 'file, 'data: 'file; type Symbol<'file> = CoffSymbol<'data, 'file, R, Coff> where Self: 'file, 'data: 'file; type SymbolIterator<'file> = CoffSymbolIterator<'data, 'file, R, Coff> where Self: 'file, 'data: 'file; type SymbolTable<'file> = CoffSymbolTable<'data, 'file, R, Coff> where Self: 'file, 'data: 'file; type DynamicRelocationIterator<'file> = NoDynamicRelocationIterator where Self: 'file, 'data: 'file; fn architecture(&self) -> Architecture { match self.header.machine() { pe::IMAGE_FILE_MACHINE_ARMNT => Architecture::Arm, pe::IMAGE_FILE_MACHINE_ARM64 | pe::IMAGE_FILE_MACHINE_ARM64EC => Architecture::Aarch64, pe::IMAGE_FILE_MACHINE_I386 => Architecture::I386, pe::IMAGE_FILE_MACHINE_AMD64 => Architecture::X86_64, _ => Architecture::Unknown, } } fn sub_architecture(&self) -> Option { match self.header.machine() { pe::IMAGE_FILE_MACHINE_ARM64EC => Some(SubArchitecture::Arm64EC), _ => None, } } #[inline] fn is_little_endian(&self) -> bool { true } #[inline] fn is_64(&self) -> bool { // Windows COFF is always 32-bit, even for 64-bit architectures. This could be confusing. false } fn kind(&self) -> ObjectKind { ObjectKind::Relocatable } fn segments(&self) -> CoffSegmentIterator<'data, '_, R, Coff> { CoffSegmentIterator { file: self, iter: self.common.sections.iter(), } } fn section_by_name_bytes<'file>( &'file self, section_name: &[u8], ) -> Option> { self.sections() .find(|section| section.name_bytes() == Ok(section_name)) } fn section_by_index(&self, index: SectionIndex) -> Result> { let section = self.common.sections.section(index)?; Ok(CoffSection { file: self, index, section, }) } fn sections(&self) -> CoffSectionIterator<'data, '_, R, Coff> { CoffSectionIterator { file: self, iter: self.common.sections.iter().enumerate(), } } fn comdats(&self) -> CoffComdatIterator<'data, '_, R, Coff> { CoffComdatIterator::new(self) } fn symbol_by_index(&self, index: SymbolIndex) -> Result> { let symbol = self.common.symbols.symbol(index)?; Ok(CoffSymbol { file: &self.common, index, symbol, }) } fn symbols(&self) -> CoffSymbolIterator<'data, '_, R, Coff> { CoffSymbolIterator::new(&self.common) } #[inline] fn symbol_table(&self) -> Option> { Some(CoffSymbolTable { file: &self.common }) } fn dynamic_symbols(&self) -> CoffSymbolIterator<'data, '_, R, Coff> { CoffSymbolIterator::empty(&self.common) } #[inline] fn dynamic_symbol_table(&self) -> Option> { None } #[inline] fn dynamic_relocations(&self) -> Option { None } #[inline] fn imports(&self) -> Result>> { // TODO: this could return undefined symbols, but not needed yet. Ok(Vec::new()) } #[inline] fn exports(&self) -> Result>> { // TODO: this could return global symbols, but not needed yet. Ok(Vec::new()) } fn has_debug_symbols(&self) -> bool { self.section_by_name(".debug_info").is_some() } fn relative_address_base(&self) -> u64 { 0 } #[inline] fn entry(&self) -> u64 { 0 } fn flags(&self) -> FileFlags { FileFlags::Coff { characteristics: self.header.characteristics(), } } } /// Read the `class_id` field from a [`pe::AnonObjectHeader`]. /// /// This can be used to determine the format of the header. pub fn anon_object_class_id<'data, R: ReadRef<'data>>(data: R) -> Result { let header = data .read_at::(0) .read_error("Invalid anon object header size or alignment")?; Ok(header.class_id) } /// A trait for generic access to [`pe::ImageFileHeader`] and [`pe::AnonObjectHeaderBigobj`]. #[allow(missing_docs)] pub trait CoffHeader: Debug + Pod { type ImageSymbol: ImageSymbol; type ImageSymbolBytes: Debug + Pod; /// Return true if this type is [`pe::AnonObjectHeaderBigobj`]. /// /// This is a property of the type, not a value in the header data. fn is_type_bigobj() -> bool; fn machine(&self) -> u16; fn number_of_sections(&self) -> u32; fn pointer_to_symbol_table(&self) -> u32; fn number_of_symbols(&self) -> u32; fn characteristics(&self) -> u16; /// Read the file header. /// /// `data` must be the entire file data. /// `offset` must be the file header offset. It is updated to point after the optional header, /// which is where the section headers are located. fn parse<'data, R: ReadRef<'data>>(data: R, offset: &mut u64) -> read::Result<&'data Self>; /// Read the section table. /// /// `data` must be the entire file data. /// `offset` must be after the optional file header. #[inline] fn sections<'data, R: ReadRef<'data>>( &self, data: R, offset: u64, ) -> read::Result> { SectionTable::parse(self, data, offset) } /// Read the symbol table and string table. /// /// `data` must be the entire file data. #[inline] fn symbols<'data, R: ReadRef<'data>>( &self, data: R, ) -> read::Result> { SymbolTable::parse(self, data) } } impl CoffHeader for pe::ImageFileHeader { type ImageSymbol = pe::ImageSymbol; type ImageSymbolBytes = pe::ImageSymbolBytes; fn is_type_bigobj() -> bool { false } fn machine(&self) -> u16 { self.machine.get(LE) } fn number_of_sections(&self) -> u32 { self.number_of_sections.get(LE).into() } fn pointer_to_symbol_table(&self) -> u32 { self.pointer_to_symbol_table.get(LE) } fn number_of_symbols(&self) -> u32 { self.number_of_symbols.get(LE) } fn characteristics(&self) -> u16 { self.characteristics.get(LE) } fn parse<'data, R: ReadRef<'data>>(data: R, offset: &mut u64) -> read::Result<&'data Self> { let header = data .read::(offset) .read_error("Invalid COFF file header size or alignment")?; // Skip over the optional header. *offset = offset .checked_add(header.size_of_optional_header.get(LE).into()) .read_error("Invalid COFF optional header size")?; // TODO: maybe validate that the machine is known? Ok(header) } } impl CoffHeader for pe::AnonObjectHeaderBigobj { type ImageSymbol = pe::ImageSymbolEx; type ImageSymbolBytes = pe::ImageSymbolExBytes; fn is_type_bigobj() -> bool { true } fn machine(&self) -> u16 { self.machine.get(LE) } fn number_of_sections(&self) -> u32 { self.number_of_sections.get(LE) } fn pointer_to_symbol_table(&self) -> u32 { self.pointer_to_symbol_table.get(LE) } fn number_of_symbols(&self) -> u32 { self.number_of_symbols.get(LE) } fn characteristics(&self) -> u16 { 0 } fn parse<'data, R: ReadRef<'data>>(data: R, offset: &mut u64) -> read::Result<&'data Self> { let header = data .read::(offset) .read_error("Invalid COFF bigobj file header size or alignment")?; if header.sig1.get(LE) != pe::IMAGE_FILE_MACHINE_UNKNOWN || header.sig2.get(LE) != 0xffff || header.version.get(LE) < 2 || header.class_id != pe::ANON_OBJECT_HEADER_BIGOBJ_CLASS_ID { return Err(read::Error("Invalid COFF bigobj header values")); } // TODO: maybe validate that the machine is known? Ok(header) } } object-0.36.5/src/read/coff/import.rs000064400000000000000000000162771046102023000155010ustar 00000000000000//! Support for reading short import files. //! //! These are used by some Windows linkers as a more compact way to describe //! dynamically imported symbols. use crate::endian::LittleEndian as LE; use crate::pe; use crate::read::{ Architecture, ByteString, Bytes, Error, ReadError, ReadRef, Result, SubArchitecture, }; /// A Windows short form description of a symbol to import. /// /// Used in Windows import libraries to provide a mapping from /// a symbol name to a DLL export. This is not an object file. /// /// This is a file that starts with [`pe::ImportObjectHeader`], and corresponds /// to [`crate::FileKind::CoffImport`]. #[derive(Debug, Clone)] pub struct ImportFile<'data> { header: &'data pe::ImportObjectHeader, kind: ImportType, dll: ByteString<'data>, symbol: ByteString<'data>, import: Option>, } impl<'data> ImportFile<'data> { /// Parse it. pub fn parse>(data: R) -> Result { let mut offset = 0; let header = pe::ImportObjectHeader::parse(data, &mut offset)?; let data = header.parse_data(data, &mut offset)?; // Unmangles a name by removing a `?`, `@` or `_` prefix. fn strip_prefix(s: &[u8]) -> &[u8] { match s.split_first() { Some((b, rest)) if [b'?', b'@', b'_'].contains(b) => rest, _ => s, } } Ok(Self { header, dll: data.dll, symbol: data.symbol, kind: match header.import_type() { pe::IMPORT_OBJECT_CODE => ImportType::Code, pe::IMPORT_OBJECT_DATA => ImportType::Data, pe::IMPORT_OBJECT_CONST => ImportType::Const, _ => return Err(Error("Invalid COFF import library import type")), }, import: match header.name_type() { pe::IMPORT_OBJECT_ORDINAL => None, pe::IMPORT_OBJECT_NAME => Some(data.symbol()), pe::IMPORT_OBJECT_NAME_NO_PREFIX => Some(strip_prefix(data.symbol())), pe::IMPORT_OBJECT_NAME_UNDECORATE => Some( strip_prefix(data.symbol()) .split(|&b| b == b'@') .next() .unwrap(), ), pe::IMPORT_OBJECT_NAME_EXPORTAS => data.export(), _ => return Err(Error("Unknown COFF import library name type")), } .map(ByteString), }) } /// Get the machine type. pub fn architecture(&self) -> Architecture { match self.header.machine.get(LE) { pe::IMAGE_FILE_MACHINE_ARMNT => Architecture::Arm, pe::IMAGE_FILE_MACHINE_ARM64 | pe::IMAGE_FILE_MACHINE_ARM64EC => Architecture::Aarch64, pe::IMAGE_FILE_MACHINE_I386 => Architecture::I386, pe::IMAGE_FILE_MACHINE_AMD64 => Architecture::X86_64, _ => Architecture::Unknown, } } /// Get the sub machine type, if available. pub fn sub_architecture(&self) -> Option { match self.header.machine.get(LE) { pe::IMAGE_FILE_MACHINE_ARM64EC => Some(SubArchitecture::Arm64EC), _ => None, } } /// The public symbol name. pub fn symbol(&self) -> &'data [u8] { self.symbol.0 } /// The name of the DLL to import the symbol from. pub fn dll(&self) -> &'data [u8] { self.dll.0 } /// The name exported from the DLL. pub fn import(&self) -> ImportName<'data> { match self.import { Some(name) => ImportName::Name(name.0), None => ImportName::Ordinal(self.header.ordinal_or_hint.get(LE)), } } /// The type of import. Usually either a function or data. pub fn import_type(&self) -> ImportType { self.kind } } /// The name or ordinal to import from a DLL. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ImportName<'data> { /// Import by ordinal. Ordinarily this is a 1-based index. Ordinal(u16), /// Import by name. Name(&'data [u8]), } /// The kind of import symbol. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ImportType { /// An executable code symbol. Code, /// A data symbol. Data, /// A constant value. Const, } impl pe::ImportObjectHeader { /// Read the short import header. /// /// Also checks that the signature and version are valid. /// Directly following this header will be the string data. pub fn parse<'data, R: ReadRef<'data>>(data: R, offset: &mut u64) -> Result<&'data Self> { let header = data .read::(offset) .read_error("Invalid COFF import library header size")?; if header.sig1.get(LE) != 0 || header.sig2.get(LE) != pe::IMPORT_OBJECT_HDR_SIG2 { Err(Error("Invalid COFF import library header")) } else if header.version.get(LE) != 0 { Err(Error("Unknown COFF import library header version")) } else { Ok(header) } } /// Parse the data following the header. pub fn parse_data<'data, R: ReadRef<'data>>( &self, data: R, offset: &mut u64, ) -> Result> { let mut data = Bytes( data.read_bytes(offset, u64::from(self.size_of_data.get(LE))) .read_error("Invalid COFF import library data size")?, ); let symbol = data .read_string() .map(ByteString) .read_error("Could not read COFF import library symbol name")?; let dll = data .read_string() .map(ByteString) .read_error("Could not read COFF import library DLL name")?; let export = if self.name_type() == pe::IMPORT_OBJECT_NAME_EXPORTAS { data.read_string() .map(ByteString) .map(Some) .read_error("Could not read COFF import library export name")? } else { None }; Ok(ImportObjectData { symbol, dll, export, }) } /// The type of import. /// /// This is one of the `IMPORT_OBJECT_*` constants. pub fn import_type(&self) -> u16 { self.name_type.get(LE) & pe::IMPORT_OBJECT_TYPE_MASK } /// The type of import name. /// /// This is one of the `IMPORT_OBJECT_*` constants. pub fn name_type(&self) -> u16 { (self.name_type.get(LE) >> pe::IMPORT_OBJECT_NAME_SHIFT) & pe::IMPORT_OBJECT_NAME_MASK } } /// The data following [`pe::ImportObjectHeader`]. #[derive(Debug, Clone)] pub struct ImportObjectData<'data> { symbol: ByteString<'data>, dll: ByteString<'data>, export: Option>, } impl<'data> ImportObjectData<'data> { /// The public symbol name. pub fn symbol(&self) -> &'data [u8] { self.symbol.0 } /// The name of the DLL to import the symbol from. pub fn dll(&self) -> &'data [u8] { self.dll.0 } /// The name exported from the DLL. /// /// This is only set if the name is not derived from the symbol name. pub fn export(&self) -> Option<&'data [u8]> { self.export.map(|export| export.0) } } object-0.36.5/src/read/coff/mod.rs000064400000000000000000000037621046102023000147410ustar 00000000000000//! Support for reading Windows COFF files. //! //! Traits are used to abstract over the difference between COFF object files //! and COFF bigobj files. The primary trait for this is [`CoffHeader`]. //! //! ## High level API //! //! [`CoffFile`] implements the [`Object`](crate::read::Object) trait for //! COFF files. [`CoffFile`] is parameterised by [`CoffHeader`]. //! The default parameter allows reading regular COFF object files, //! while the type alias [`CoffBigFile`] allows reading COFF bigobj files. //! //! [`ImportFile`] allows reading COFF short imports that are used in import //! libraries. Currently these are not integrated with the unified read API. //! //! ## Low level API //! //! The [`CoffHeader`] trait can be directly used to parse both COFF //! object files (which start with [`pe::ImageFileHeader`]) and COFF bigobj //! files (which start with [`pe::AnonObjectHeaderBigobj`]). //! //! ### Example for low level API //! ```no_run //! use object::pe; //! use object::read::coff::{CoffHeader, ImageSymbol as _}; //! use std::error::Error; //! use std::fs; //! //! /// Reads a file and displays the name of each section and symbol. //! fn main() -> Result<(), Box> { //! # #[cfg(feature = "std")] { //! let data = fs::read("path/to/binary")?; //! let mut offset = 0; //! let header = pe::ImageFileHeader::parse(&*data, &mut offset)?; //! let sections = header.sections(&*data, offset)?; //! let symbols = header.symbols(&*data)?; //! for section in sections.iter() { //! println!("{}", String::from_utf8_lossy(section.name(symbols.strings())?)); //! } //! for (_index, symbol) in symbols.iter() { //! println!("{}", String::from_utf8_lossy(symbol.name(symbols.strings())?)); //! } //! # } //! Ok(()) //! } //! ``` #[cfg(doc)] use crate::pe; mod file; pub use file::*; mod section; pub use section::*; mod symbol; pub use symbol::*; mod relocation; pub use relocation::*; mod comdat; pub use comdat::*; mod import; pub use import::*; object-0.36.5/src/read/coff/relocation.rs000064400000000000000000000123071046102023000163140ustar 00000000000000use alloc::fmt; use core::slice; use crate::endian::LittleEndian as LE; use crate::pe; use crate::read::{ ReadRef, Relocation, RelocationEncoding, RelocationFlags, RelocationKind, RelocationTarget, SymbolIndex, }; use super::{CoffFile, CoffHeader}; /// An iterator for the relocations in a [`CoffBigSection`](super::CoffBigSection). pub type CoffBigRelocationIterator<'data, 'file, R = &'data [u8]> = CoffRelocationIterator<'data, 'file, R, pe::AnonObjectHeaderBigobj>; /// An iterator for the relocations in a [`CoffSection`](super::CoffSection). pub struct CoffRelocationIterator< 'data, 'file, R: ReadRef<'data> = &'data [u8], Coff: CoffHeader = pe::ImageFileHeader, > { pub(super) file: &'file CoffFile<'data, R, Coff>, pub(super) iter: slice::Iter<'data, pe::ImageRelocation>, } impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> Iterator for CoffRelocationIterator<'data, 'file, R, Coff> { type Item = (u64, Relocation); fn next(&mut self) -> Option { use RelocationEncoding as E; use RelocationKind as K; self.iter.next().map(|relocation| { let typ = relocation.typ.get(LE); let flags = RelocationFlags::Coff { typ }; let g = E::Generic; let unknown = (K::Unknown, E::Generic, 0, 0); let (kind, encoding, size, addend) = match self.file.header.machine() { pe::IMAGE_FILE_MACHINE_ARMNT => match typ { pe::IMAGE_REL_ARM_ADDR32 => (K::Absolute, g, 32, 0), pe::IMAGE_REL_ARM_ADDR32NB => (K::ImageOffset, g, 32, 0), pe::IMAGE_REL_ARM_REL32 => (K::Relative, g, 32, -4), pe::IMAGE_REL_ARM_SECTION => (K::SectionIndex, g, 16, 0), pe::IMAGE_REL_ARM_SECREL => (K::SectionOffset, g, 32, 0), _ => unknown, }, pe::IMAGE_FILE_MACHINE_ARM64 | pe::IMAGE_FILE_MACHINE_ARM64EC => match typ { pe::IMAGE_REL_ARM64_ADDR32 => (K::Absolute, g, 32, 0), pe::IMAGE_REL_ARM64_ADDR32NB => (K::ImageOffset, g, 32, 0), pe::IMAGE_REL_ARM64_SECREL => (K::SectionOffset, g, 32, 0), pe::IMAGE_REL_ARM64_SECTION => (K::SectionIndex, g, 16, 0), pe::IMAGE_REL_ARM64_ADDR64 => (K::Absolute, g, 64, 0), pe::IMAGE_REL_ARM64_REL32 => (K::Relative, g, 32, -4), pe::IMAGE_REL_ARM64_BRANCH26 => (K::Relative, E::AArch64Call, 26, 0), _ => unknown, }, pe::IMAGE_FILE_MACHINE_I386 => match typ { pe::IMAGE_REL_I386_DIR16 => (K::Absolute, g, 16, 0), pe::IMAGE_REL_I386_REL16 => (K::Relative, g, 16, 0), pe::IMAGE_REL_I386_DIR32 => (K::Absolute, g, 32, 0), pe::IMAGE_REL_I386_DIR32NB => (K::ImageOffset, g, 32, 0), pe::IMAGE_REL_I386_SECTION => (K::SectionIndex, g, 16, 0), pe::IMAGE_REL_I386_SECREL => (K::SectionOffset, g, 32, 0), pe::IMAGE_REL_I386_SECREL7 => (K::SectionOffset, g, 7, 0), pe::IMAGE_REL_I386_REL32 => (K::Relative, g, 32, -4), _ => unknown, }, pe::IMAGE_FILE_MACHINE_AMD64 => match typ { pe::IMAGE_REL_AMD64_ADDR64 => (K::Absolute, g, 64, 0), pe::IMAGE_REL_AMD64_ADDR32 => (K::Absolute, g, 32, 0), pe::IMAGE_REL_AMD64_ADDR32NB => (K::ImageOffset, g, 32, 0), pe::IMAGE_REL_AMD64_REL32 => (K::Relative, g, 32, -4), pe::IMAGE_REL_AMD64_REL32_1 => (K::Relative, g, 32, -5), pe::IMAGE_REL_AMD64_REL32_2 => (K::Relative, g, 32, -6), pe::IMAGE_REL_AMD64_REL32_3 => (K::Relative, g, 32, -7), pe::IMAGE_REL_AMD64_REL32_4 => (K::Relative, g, 32, -8), pe::IMAGE_REL_AMD64_REL32_5 => (K::Relative, g, 32, -9), pe::IMAGE_REL_AMD64_SECTION => (K::SectionIndex, g, 16, 0), pe::IMAGE_REL_AMD64_SECREL => (K::SectionOffset, g, 32, 0), pe::IMAGE_REL_AMD64_SECREL7 => (K::SectionOffset, g, 7, 0), _ => unknown, }, _ => unknown, }; let target = RelocationTarget::Symbol(relocation.symbol()); ( u64::from(relocation.virtual_address.get(LE)), Relocation { kind, encoding, size, target, addend, implicit_addend: true, flags, }, ) }) } } impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> fmt::Debug for CoffRelocationIterator<'data, 'file, R, Coff> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("CoffRelocationIterator").finish() } } impl pe::ImageRelocation { /// Get the index of the symbol referenced by this relocation. pub fn symbol(&self) -> SymbolIndex { SymbolIndex(self.symbol_table_index.get(LE) as usize) } } object-0.36.5/src/read/coff/section.rs000064400000000000000000000463461046102023000156330ustar 00000000000000use core::convert::TryFrom; use core::{iter, result, slice, str}; use crate::endian::LittleEndian as LE; use crate::pe; use crate::read::util::StringTable; use crate::read::{ self, CompressedData, CompressedFileRange, Error, ObjectSection, ObjectSegment, ReadError, ReadRef, RelocationMap, Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags, }; use super::{CoffFile, CoffHeader, CoffRelocationIterator}; /// The table of section headers in a COFF or PE file. /// /// Returned by [`CoffHeader::sections`] and /// [`ImageNtHeaders::sections`](crate::read::pe::ImageNtHeaders::sections). #[derive(Debug, Default, Clone, Copy)] pub struct SectionTable<'data> { sections: &'data [pe::ImageSectionHeader], } impl<'data> SectionTable<'data> { /// Parse the section table. /// /// `data` must be the entire file data. /// `offset` must be after the optional file header. pub fn parse>( header: &Coff, data: R, offset: u64, ) -> Result { let sections = data .read_slice_at(offset, header.number_of_sections() as usize) .read_error("Invalid COFF/PE section headers")?; Ok(SectionTable { sections }) } /// Iterate over the section headers. /// /// Warning: section indices start at 1. #[inline] pub fn iter(&self) -> slice::Iter<'data, pe::ImageSectionHeader> { self.sections.iter() } /// Iterate over the section headers and their indices. pub fn enumerate(&self) -> impl Iterator { self.sections .iter() .enumerate() .map(|(i, section)| (SectionIndex(i + 1), section)) } /// Return true if the section table is empty. #[inline] pub fn is_empty(&self) -> bool { self.sections.is_empty() } /// The number of section headers. #[inline] pub fn len(&self) -> usize { self.sections.len() } /// Return the section header at the given index. /// /// The index is 1-based. pub fn section(&self, index: SectionIndex) -> read::Result<&'data pe::ImageSectionHeader> { self.sections .get(index.0.wrapping_sub(1)) .read_error("Invalid COFF/PE section index") } /// Return the section header with the given name. /// /// The returned index is 1-based. /// /// Ignores sections with invalid names. pub fn section_by_name>( &self, strings: StringTable<'data, R>, name: &[u8], ) -> Option<(SectionIndex, &'data pe::ImageSectionHeader)> { self.enumerate() .find(|(_, section)| section.name(strings) == Ok(name)) } /// Compute the maximum file offset used by sections. /// /// This will usually match the end of file, unless the PE file has a /// [data overlay](https://security.stackexchange.com/questions/77336/how-is-the-file-overlay-read-by-an-exe-virus) pub fn max_section_file_offset(&self) -> u64 { let mut max = 0; for section in self.iter() { match (section.pointer_to_raw_data.get(LE) as u64) .checked_add(section.size_of_raw_data.get(LE) as u64) { None => { // This cannot happen, we're suming two u32 into a u64 continue; } Some(end_of_section) => { if end_of_section > max { max = end_of_section; } } } } max } } /// An iterator for the loadable sections in a [`CoffBigFile`](super::CoffBigFile). pub type CoffBigSegmentIterator<'data, 'file, R = &'data [u8]> = CoffSegmentIterator<'data, 'file, R, pe::AnonObjectHeaderBigobj>; /// An iterator for the loadable sections in a [`CoffFile`]. #[derive(Debug)] pub struct CoffSegmentIterator< 'data, 'file, R: ReadRef<'data> = &'data [u8], Coff: CoffHeader = pe::ImageFileHeader, > { pub(super) file: &'file CoffFile<'data, R, Coff>, pub(super) iter: slice::Iter<'data, pe::ImageSectionHeader>, } impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> Iterator for CoffSegmentIterator<'data, 'file, R, Coff> { type Item = CoffSegment<'data, 'file, R, Coff>; fn next(&mut self) -> Option { self.iter.next().map(|section| CoffSegment { file: self.file, section, }) } } /// A loadable section in a [`CoffBigFile`](super::CoffBigFile). /// /// Most functionality is provided by the [`ObjectSegment`] trait implementation. pub type CoffBigSegment<'data, 'file, R = &'data [u8]> = CoffSegment<'data, 'file, R, pe::AnonObjectHeaderBigobj>; /// A loadable section in a [`CoffFile`]. /// /// Most functionality is provided by the [`ObjectSegment`] trait implementation. #[derive(Debug)] pub struct CoffSegment< 'data, 'file, R: ReadRef<'data> = &'data [u8], Coff: CoffHeader = pe::ImageFileHeader, > { pub(super) file: &'file CoffFile<'data, R, Coff>, pub(super) section: &'data pe::ImageSectionHeader, } impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> CoffSegment<'data, 'file, R, Coff> { /// Get the COFF file containing this segment. pub fn coff_file(&self) -> &'file CoffFile<'data, R, Coff> { self.file } /// Get the raw COFF section header. pub fn coff_section(&self) -> &'data pe::ImageSectionHeader { self.section } fn bytes(&self) -> Result<&'data [u8]> { self.section .coff_data(self.file.data) .read_error("Invalid COFF section offset or size") } } impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> read::private::Sealed for CoffSegment<'data, 'file, R, Coff> { } impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> ObjectSegment<'data> for CoffSegment<'data, 'file, R, Coff> { #[inline] fn address(&self) -> u64 { u64::from(self.section.virtual_address.get(LE)) } #[inline] fn size(&self) -> u64 { u64::from(self.section.virtual_size.get(LE)) } #[inline] fn align(&self) -> u64 { self.section.coff_alignment() } #[inline] fn file_range(&self) -> (u64, u64) { let (offset, size) = self.section.coff_file_range().unwrap_or((0, 0)); (u64::from(offset), u64::from(size)) } fn data(&self) -> Result<&'data [u8]> { self.bytes() } fn data_range(&self, address: u64, size: u64) -> Result> { Ok(read::util::data_range( self.bytes()?, self.address(), address, size, )) } #[inline] fn name_bytes(&self) -> Result> { self.section .name(self.file.common.symbols.strings()) .map(Some) } #[inline] fn name(&self) -> Result> { let name = self.section.name(self.file.common.symbols.strings())?; str::from_utf8(name) .ok() .read_error("Non UTF-8 COFF section name") .map(Some) } #[inline] fn flags(&self) -> SegmentFlags { let characteristics = self.section.characteristics.get(LE); SegmentFlags::Coff { characteristics } } } /// An iterator for the sections in a [`CoffBigFile`](super::CoffBigFile). pub type CoffBigSectionIterator<'data, 'file, R = &'data [u8]> = CoffSectionIterator<'data, 'file, R, pe::AnonObjectHeaderBigobj>; /// An iterator for the sections in a [`CoffFile`]. #[derive(Debug)] pub struct CoffSectionIterator< 'data, 'file, R: ReadRef<'data> = &'data [u8], Coff: CoffHeader = pe::ImageFileHeader, > { pub(super) file: &'file CoffFile<'data, R, Coff>, pub(super) iter: iter::Enumerate>, } impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> Iterator for CoffSectionIterator<'data, 'file, R, Coff> { type Item = CoffSection<'data, 'file, R, Coff>; fn next(&mut self) -> Option { self.iter.next().map(|(index, section)| CoffSection { file: self.file, index: SectionIndex(index + 1), section, }) } } /// A section in a [`CoffBigFile`](super::CoffBigFile). /// /// Most functionality is provided by the [`ObjectSection`] trait implementation. pub type CoffBigSection<'data, 'file, R = &'data [u8]> = CoffSection<'data, 'file, R, pe::AnonObjectHeaderBigobj>; /// A section in a [`CoffFile`]. /// /// Most functionality is provided by the [`ObjectSection`] trait implementation. #[derive(Debug)] pub struct CoffSection< 'data, 'file, R: ReadRef<'data> = &'data [u8], Coff: CoffHeader = pe::ImageFileHeader, > { pub(super) file: &'file CoffFile<'data, R, Coff>, pub(super) index: SectionIndex, pub(super) section: &'data pe::ImageSectionHeader, } impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> CoffSection<'data, 'file, R, Coff> { /// Get the COFF file containing this section. pub fn coff_file(&self) -> &'file CoffFile<'data, R, Coff> { self.file } /// Get the raw COFF section header. pub fn coff_section(&self) -> &'data pe::ImageSectionHeader { self.section } /// Get the raw COFF relocations for this section. pub fn coff_relocations(&self) -> Result<&'data [pe::ImageRelocation]> { self.section.coff_relocations(self.file.data) } fn bytes(&self) -> Result<&'data [u8]> { self.section .coff_data(self.file.data) .read_error("Invalid COFF section offset or size") } } impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> read::private::Sealed for CoffSection<'data, 'file, R, Coff> { } impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> ObjectSection<'data> for CoffSection<'data, 'file, R, Coff> { type RelocationIterator = CoffRelocationIterator<'data, 'file, R, Coff>; #[inline] fn index(&self) -> SectionIndex { self.index } #[inline] fn address(&self) -> u64 { u64::from(self.section.virtual_address.get(LE)) } #[inline] fn size(&self) -> u64 { // TODO: This may need to be the length from the auxiliary symbol for this section. u64::from(self.section.size_of_raw_data.get(LE)) } #[inline] fn align(&self) -> u64 { self.section.coff_alignment() } #[inline] fn file_range(&self) -> Option<(u64, u64)> { let (offset, size) = self.section.coff_file_range()?; Some((u64::from(offset), u64::from(size))) } fn data(&self) -> Result<&'data [u8]> { self.bytes() } fn data_range(&self, address: u64, size: u64) -> Result> { Ok(read::util::data_range( self.bytes()?, self.address(), address, size, )) } #[inline] fn compressed_file_range(&self) -> Result { Ok(CompressedFileRange::none(self.file_range())) } #[inline] fn compressed_data(&self) -> Result> { self.data().map(CompressedData::none) } #[inline] fn name_bytes(&self) -> Result<&'data [u8]> { self.section.name(self.file.common.symbols.strings()) } #[inline] fn name(&self) -> Result<&'data str> { let name = self.name_bytes()?; str::from_utf8(name) .ok() .read_error("Non UTF-8 COFF section name") } #[inline] fn segment_name_bytes(&self) -> Result> { Ok(None) } #[inline] fn segment_name(&self) -> Result> { Ok(None) } #[inline] fn kind(&self) -> SectionKind { self.section.kind() } fn relocations(&self) -> CoffRelocationIterator<'data, 'file, R, Coff> { let relocations = self.coff_relocations().unwrap_or(&[]); CoffRelocationIterator { file: self.file, iter: relocations.iter(), } } fn relocation_map(&self) -> read::Result { RelocationMap::new(self.file, self) } fn flags(&self) -> SectionFlags { SectionFlags::Coff { characteristics: self.section.characteristics.get(LE), } } } impl pe::ImageSectionHeader { pub(crate) fn kind(&self) -> SectionKind { let characteristics = self.characteristics.get(LE); if characteristics & (pe::IMAGE_SCN_CNT_CODE | pe::IMAGE_SCN_MEM_EXECUTE) != 0 { SectionKind::Text } else if characteristics & pe::IMAGE_SCN_CNT_INITIALIZED_DATA != 0 { if characteristics & pe::IMAGE_SCN_MEM_DISCARDABLE != 0 { SectionKind::Other } else if characteristics & pe::IMAGE_SCN_MEM_WRITE != 0 { SectionKind::Data } else { SectionKind::ReadOnlyData } } else if characteristics & pe::IMAGE_SCN_CNT_UNINITIALIZED_DATA != 0 { SectionKind::UninitializedData } else if characteristics & pe::IMAGE_SCN_LNK_INFO != 0 { SectionKind::Linker } else { SectionKind::Unknown } } } impl pe::ImageSectionHeader { /// Return the string table offset of the section name. /// /// Returns `Ok(None)` if the name doesn't use the string table /// and can be obtained with `raw_name` instead. pub fn name_offset(&self) -> Result> { let bytes = &self.name; if bytes[0] != b'/' { return Ok(None); } if bytes[1] == b'/' { let mut offset = 0; for byte in bytes[2..].iter() { let digit = match byte { b'A'..=b'Z' => byte - b'A', b'a'..=b'z' => byte - b'a' + 26, b'0'..=b'9' => byte - b'0' + 52, b'+' => 62, b'/' => 63, _ => return Err(Error("Invalid COFF section name base-64 offset")), }; offset = offset * 64 + digit as u64; } u32::try_from(offset) .ok() .read_error("Invalid COFF section name base-64 offset") .map(Some) } else { let mut offset = 0; for byte in bytes[1..].iter() { let digit = match byte { b'0'..=b'9' => byte - b'0', 0 => break, _ => return Err(Error("Invalid COFF section name base-10 offset")), }; offset = offset * 10 + digit as u32; } Ok(Some(offset)) } } /// Return the section name. /// /// This handles decoding names that are offsets into the symbol string table. pub fn name<'data, R: ReadRef<'data>>( &'data self, strings: StringTable<'data, R>, ) -> Result<&'data [u8]> { if let Some(offset) = self.name_offset()? { strings .get(offset) .read_error("Invalid COFF section name offset") } else { Ok(self.raw_name()) } } /// Return the raw section name. pub fn raw_name(&self) -> &[u8] { let bytes = &self.name; match memchr::memchr(b'\0', bytes) { Some(end) => &bytes[..end], None => &bytes[..], } } /// Return the offset and size of the section in a COFF file. /// /// Returns `None` for sections that have no data in the file. pub fn coff_file_range(&self) -> Option<(u32, u32)> { if self.characteristics.get(LE) & pe::IMAGE_SCN_CNT_UNINITIALIZED_DATA != 0 { None } else { let offset = self.pointer_to_raw_data.get(LE); // Note: virtual size is not used for COFF. let size = self.size_of_raw_data.get(LE); Some((offset, size)) } } /// Return the section data in a COFF file. /// /// Returns `Ok(&[])` if the section has no data. /// Returns `Err` for invalid values. pub fn coff_data<'data, R: ReadRef<'data>>(&self, data: R) -> result::Result<&'data [u8], ()> { if let Some((offset, size)) = self.coff_file_range() { data.read_bytes_at(offset.into(), size.into()) } else { Ok(&[]) } } /// Return the section alignment in bytes. /// /// This is only valid for sections in a COFF file. pub fn coff_alignment(&self) -> u64 { match self.characteristics.get(LE) & pe::IMAGE_SCN_ALIGN_MASK { pe::IMAGE_SCN_ALIGN_1BYTES => 1, pe::IMAGE_SCN_ALIGN_2BYTES => 2, pe::IMAGE_SCN_ALIGN_4BYTES => 4, pe::IMAGE_SCN_ALIGN_8BYTES => 8, pe::IMAGE_SCN_ALIGN_16BYTES => 16, pe::IMAGE_SCN_ALIGN_32BYTES => 32, pe::IMAGE_SCN_ALIGN_64BYTES => 64, pe::IMAGE_SCN_ALIGN_128BYTES => 128, pe::IMAGE_SCN_ALIGN_256BYTES => 256, pe::IMAGE_SCN_ALIGN_512BYTES => 512, pe::IMAGE_SCN_ALIGN_1024BYTES => 1024, pe::IMAGE_SCN_ALIGN_2048BYTES => 2048, pe::IMAGE_SCN_ALIGN_4096BYTES => 4096, pe::IMAGE_SCN_ALIGN_8192BYTES => 8192, _ => 16, } } /// Read the relocations in a COFF file. /// /// `data` must be the entire file data. pub fn coff_relocations<'data, R: ReadRef<'data>>( &self, data: R, ) -> read::Result<&'data [pe::ImageRelocation]> { let mut pointer = self.pointer_to_relocations.get(LE).into(); let mut number: usize = self.number_of_relocations.get(LE).into(); if number == u16::MAX.into() && self.characteristics.get(LE) & pe::IMAGE_SCN_LNK_NRELOC_OVFL != 0 { // Extended relocations. Read first relocation (which contains extended count) & adjust // relocations pointer. let extended_relocation_info = data .read_at::(pointer) .read_error("Invalid COFF relocation offset or number")?; number = extended_relocation_info.virtual_address.get(LE) as usize; if number == 0 { return Err(Error("Invalid COFF relocation number")); } pointer += core::mem::size_of::() as u64; // Extended relocation info does not contribute to the count of sections. number -= 1; } data.read_slice_at(pointer, number) .read_error("Invalid COFF relocation offset or number") } } #[cfg(test)] mod tests { use super::*; #[test] fn name_offset() { let mut section = pe::ImageSectionHeader::default(); section.name = *b"xxxxxxxx"; assert_eq!(section.name_offset(), Ok(None)); section.name = *b"/0\0\0\0\0\0\0"; assert_eq!(section.name_offset(), Ok(Some(0))); section.name = *b"/9999999"; assert_eq!(section.name_offset(), Ok(Some(999_9999))); section.name = *b"//AAAAAA"; assert_eq!(section.name_offset(), Ok(Some(0))); section.name = *b"//D/////"; assert_eq!(section.name_offset(), Ok(Some(0xffff_ffff))); section.name = *b"//EAAAAA"; assert!(section.name_offset().is_err()); section.name = *b"////////"; assert!(section.name_offset().is_err()); } } object-0.36.5/src/read/coff/symbol.rs000064400000000000000000000524351046102023000154700ustar 00000000000000use alloc::fmt; use alloc::vec::Vec; use core::convert::TryInto; use core::fmt::Debug; use core::str; use super::{CoffCommon, CoffHeader, SectionTable}; use crate::endian::{LittleEndian as LE, U32Bytes}; use crate::pe; use crate::pod::{bytes_of, bytes_of_slice, Pod}; use crate::read::util::StringTable; use crate::read::{ self, Bytes, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, Result, SectionIndex, SymbolFlags, SymbolIndex, SymbolKind, SymbolMap, SymbolMapEntry, SymbolScope, SymbolSection, }; /// A table of symbol entries in a COFF or PE file. /// /// Also includes the string table used for the symbol names. /// /// Returned by [`CoffHeader::symbols`] and /// [`ImageNtHeaders::symbols`](crate::read::pe::ImageNtHeaders::symbols). #[derive(Debug)] pub struct SymbolTable<'data, R = &'data [u8], Coff = pe::ImageFileHeader> where R: ReadRef<'data>, Coff: CoffHeader, { symbols: &'data [Coff::ImageSymbolBytes], strings: StringTable<'data, R>, } impl<'data, R: ReadRef<'data>, Coff: CoffHeader> Default for SymbolTable<'data, R, Coff> { fn default() -> Self { Self { symbols: &[], strings: StringTable::default(), } } } impl<'data, R: ReadRef<'data>, Coff: CoffHeader> SymbolTable<'data, R, Coff> { /// Read the symbol table. pub fn parse(header: &Coff, data: R) -> Result { // The symbol table may not be present. let mut offset = header.pointer_to_symbol_table().into(); let (symbols, strings) = if offset != 0 { let symbols = data .read_slice(&mut offset, header.number_of_symbols() as usize) .read_error("Invalid COFF symbol table offset or size")?; // Note: don't update data when reading length; the length includes itself. let length = data .read_at::>(offset) .read_error("Missing COFF string table")? .get(LE); let str_end = offset .checked_add(length as u64) .read_error("Invalid COFF string table length")?; let strings = StringTable::new(data, offset, str_end); (symbols, strings) } else { (&[][..], StringTable::default()) }; Ok(SymbolTable { symbols, strings }) } /// Return the string table used for the symbol names. #[inline] pub fn strings(&self) -> StringTable<'data, R> { self.strings } /// Return true if the symbol table is empty. #[inline] pub fn is_empty(&self) -> bool { self.symbols.is_empty() } /// The number of symbol table entries. /// /// This includes auxiliary symbol table entries. #[inline] pub fn len(&self) -> usize { self.symbols.len() } /// Iterate over the symbols. #[inline] pub fn iter<'table>(&'table self) -> SymbolIterator<'data, 'table, R, Coff> { SymbolIterator { symbols: self, index: SymbolIndex(0), } } /// Return the symbol table entry at the given index. #[inline] pub fn symbol(&self, index: SymbolIndex) -> Result<&'data Coff::ImageSymbol> { self.get::(index, 0) } /// Return the auxiliary function symbol for the symbol table entry at the given index. /// /// Note that the index is of the symbol, not the first auxiliary record. #[inline] pub fn aux_function(&self, index: SymbolIndex) -> Result<&'data pe::ImageAuxSymbolFunction> { self.get::(index, 1) } /// Return the auxiliary section symbol for the symbol table entry at the given index. /// /// Note that the index is of the symbol, not the first auxiliary record. #[inline] pub fn aux_section(&self, index: SymbolIndex) -> Result<&'data pe::ImageAuxSymbolSection> { self.get::(index, 1) } /// Return the auxiliary file name for the symbol table entry at the given index. /// /// Note that the index is of the symbol, not the first auxiliary record. pub fn aux_file_name(&self, index: SymbolIndex, aux_count: u8) -> Result<&'data [u8]> { let entries = index .0 .checked_add(1) .and_then(|x| Some(x..x.checked_add(aux_count.into())?)) .and_then(|x| self.symbols.get(x)) .read_error("Invalid COFF symbol index")?; let bytes = bytes_of_slice(entries); // The name is padded with nulls. Ok(match memchr::memchr(b'\0', bytes) { Some(end) => &bytes[..end], None => bytes, }) } /// Return the symbol table entry or auxiliary record at the given index and offset. pub fn get(&self, index: SymbolIndex, offset: usize) -> Result<&'data T> { let bytes = index .0 .checked_add(offset) .and_then(|x| self.symbols.get(x)) .read_error("Invalid COFF symbol index")?; Bytes(bytes_of(bytes)) .read() .read_error("Invalid COFF symbol data") } /// Construct a map from addresses to a user-defined map entry. pub fn map Option>( &self, f: F, ) -> SymbolMap { let mut symbols = Vec::with_capacity(self.symbols.len()); for (_, symbol) in self.iter() { if !symbol.is_definition() { continue; } if let Some(entry) = f(symbol) { symbols.push(entry); } } SymbolMap::new(symbols) } } /// An iterator for symbol entries in a COFF or PE file. /// /// Yields the index and symbol structure for each symbol. #[derive(Debug)] pub struct SymbolIterator<'data, 'table, R = &'data [u8], Coff = pe::ImageFileHeader> where R: ReadRef<'data>, Coff: CoffHeader, { symbols: &'table SymbolTable<'data, R, Coff>, index: SymbolIndex, } impl<'data, 'table, R: ReadRef<'data>, Coff: CoffHeader> Iterator for SymbolIterator<'data, 'table, R, Coff> { type Item = (SymbolIndex, &'data Coff::ImageSymbol); fn next(&mut self) -> Option { let index = self.index; let symbol = self.symbols.symbol(index).ok()?; self.index.0 += 1 + symbol.number_of_aux_symbols() as usize; Some((index, symbol)) } } /// A symbol table in a [`CoffBigFile`](super::CoffBigFile). pub type CoffBigSymbolTable<'data, 'file, R = &'data [u8]> = CoffSymbolTable<'data, 'file, R, pe::AnonObjectHeaderBigobj>; /// A symbol table in a [`CoffFile`](super::CoffFile) /// or [`PeFile`](crate::read::pe::PeFile). #[derive(Debug, Clone, Copy)] pub struct CoffSymbolTable<'data, 'file, R = &'data [u8], Coff = pe::ImageFileHeader> where R: ReadRef<'data>, Coff: CoffHeader, { pub(crate) file: &'file CoffCommon<'data, R, Coff>, } impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> read::private::Sealed for CoffSymbolTable<'data, 'file, R, Coff> { } impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> ObjectSymbolTable<'data> for CoffSymbolTable<'data, 'file, R, Coff> { type Symbol = CoffSymbol<'data, 'file, R, Coff>; type SymbolIterator = CoffSymbolIterator<'data, 'file, R, Coff>; fn symbols(&self) -> Self::SymbolIterator { CoffSymbolIterator::new(self.file) } fn symbol_by_index(&self, index: SymbolIndex) -> Result { let symbol = self.file.symbols.symbol(index)?; Ok(CoffSymbol { file: self.file, index, symbol, }) } } /// An iterator for the symbols in a [`CoffBigFile`](super::CoffBigFile). pub type CoffBigSymbolIterator<'data, 'file, R = &'data [u8]> = CoffSymbolIterator<'data, 'file, R, pe::AnonObjectHeaderBigobj>; /// An iterator for the symbols in a [`CoffFile`](super::CoffFile) /// or [`PeFile`](crate::read::pe::PeFile). pub struct CoffSymbolIterator<'data, 'file, R = &'data [u8], Coff = pe::ImageFileHeader> where R: ReadRef<'data>, Coff: CoffHeader, { file: &'file CoffCommon<'data, R, Coff>, index: SymbolIndex, } impl<'data, 'file, R, Coff> CoffSymbolIterator<'data, 'file, R, Coff> where R: ReadRef<'data>, Coff: CoffHeader, { pub(crate) fn new(file: &'file CoffCommon<'data, R, Coff>) -> Self { Self { file, index: SymbolIndex(0), } } pub(crate) fn empty(file: &'file CoffCommon<'data, R, Coff>) -> Self { Self { file, index: SymbolIndex(file.symbols.len()), } } } impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> fmt::Debug for CoffSymbolIterator<'data, 'file, R, Coff> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("CoffSymbolIterator").finish() } } impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> Iterator for CoffSymbolIterator<'data, 'file, R, Coff> { type Item = CoffSymbol<'data, 'file, R, Coff>; fn next(&mut self) -> Option { let index = self.index; let symbol = self.file.symbols.symbol(index).ok()?; self.index.0 += 1 + symbol.number_of_aux_symbols() as usize; Some(CoffSymbol { file: self.file, index, symbol, }) } } /// A symbol in a [`CoffBigFile`](super::CoffBigFile). /// /// Most functionality is provided by the [`ObjectSymbol`] trait implementation. pub type CoffBigSymbol<'data, 'file, R = &'data [u8]> = CoffSymbol<'data, 'file, R, pe::AnonObjectHeaderBigobj>; /// A symbol in a [`CoffFile`](super::CoffFile) or [`PeFile`](crate::read::pe::PeFile). /// /// Most functionality is provided by the [`ObjectSymbol`] trait implementation. #[derive(Debug, Clone, Copy)] pub struct CoffSymbol<'data, 'file, R = &'data [u8], Coff = pe::ImageFileHeader> where R: ReadRef<'data>, Coff: CoffHeader, { pub(crate) file: &'file CoffCommon<'data, R, Coff>, pub(crate) index: SymbolIndex, pub(crate) symbol: &'data Coff::ImageSymbol, } impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> CoffSymbol<'data, 'file, R, Coff> { #[inline] /// Get the raw `ImageSymbol` struct. #[deprecated(note = "Use `coff_symbol` instead")] pub fn raw_symbol(&self) -> &'data Coff::ImageSymbol { self.symbol } /// Get the raw `ImageSymbol` struct. pub fn coff_symbol(&self) -> &'data Coff::ImageSymbol { self.symbol } } impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> read::private::Sealed for CoffSymbol<'data, 'file, R, Coff> { } impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> ObjectSymbol<'data> for CoffSymbol<'data, 'file, R, Coff> { #[inline] fn index(&self) -> SymbolIndex { self.index } fn name_bytes(&self) -> read::Result<&'data [u8]> { if self.symbol.has_aux_file_name() { self.file .symbols .aux_file_name(self.index, self.symbol.number_of_aux_symbols()) } else { self.symbol.name(self.file.symbols.strings()) } } fn name(&self) -> read::Result<&'data str> { let name = self.name_bytes()?; str::from_utf8(name) .ok() .read_error("Non UTF-8 COFF symbol name") } fn address(&self) -> u64 { self.symbol .address(self.file.image_base, &self.file.sections) .unwrap_or(None) .unwrap_or(0) } fn size(&self) -> u64 { match self.symbol.storage_class() { pe::IMAGE_SYM_CLASS_STATIC => { // Section symbols may duplicate the size from the section table. if self.symbol.has_aux_section() { if let Ok(aux) = self.file.symbols.aux_section(self.index) { u64::from(aux.length.get(LE)) } else { 0 } } else { 0 } } pe::IMAGE_SYM_CLASS_EXTERNAL => { if self.symbol.section_number() == pe::IMAGE_SYM_UNDEFINED { // For undefined symbols, symbol.value is 0 and the size is 0. // For common data, symbol.value is the size. u64::from(self.symbol.value()) } else if self.symbol.has_aux_function() { // Function symbols may have a size. if let Ok(aux) = self.file.symbols.aux_function(self.index) { u64::from(aux.total_size.get(LE)) } else { 0 } } else { 0 } } // Most symbols don't have sizes. _ => 0, } } fn kind(&self) -> SymbolKind { let derived_kind = if self.symbol.derived_type() == pe::IMAGE_SYM_DTYPE_FUNCTION { SymbolKind::Text } else { SymbolKind::Data }; match self.symbol.storage_class() { pe::IMAGE_SYM_CLASS_STATIC => { if self.symbol.has_aux_section() { SymbolKind::Section } else { derived_kind } } pe::IMAGE_SYM_CLASS_EXTERNAL | pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL => derived_kind, pe::IMAGE_SYM_CLASS_SECTION => SymbolKind::Section, pe::IMAGE_SYM_CLASS_FILE => SymbolKind::File, pe::IMAGE_SYM_CLASS_LABEL => SymbolKind::Label, _ => SymbolKind::Unknown, } } fn section(&self) -> SymbolSection { match self.symbol.section_number() { pe::IMAGE_SYM_UNDEFINED => { if self.symbol.storage_class() == pe::IMAGE_SYM_CLASS_EXTERNAL { if self.symbol.value() == 0 { SymbolSection::Undefined } else { SymbolSection::Common } } else if self.symbol.storage_class() == pe::IMAGE_SYM_CLASS_SECTION { SymbolSection::Undefined } else { SymbolSection::Unknown } } pe::IMAGE_SYM_ABSOLUTE => SymbolSection::Absolute, pe::IMAGE_SYM_DEBUG => { if self.symbol.storage_class() == pe::IMAGE_SYM_CLASS_FILE { SymbolSection::None } else { SymbolSection::Unknown } } index if index > 0 => SymbolSection::Section(SectionIndex(index as usize)), _ => SymbolSection::Unknown, } } #[inline] fn is_undefined(&self) -> bool { self.symbol.storage_class() == pe::IMAGE_SYM_CLASS_EXTERNAL && self.symbol.section_number() == pe::IMAGE_SYM_UNDEFINED && self.symbol.value() == 0 } #[inline] fn is_definition(&self) -> bool { self.symbol.is_definition() } #[inline] fn is_common(&self) -> bool { self.symbol.storage_class() == pe::IMAGE_SYM_CLASS_EXTERNAL && self.symbol.section_number() == pe::IMAGE_SYM_UNDEFINED && self.symbol.value() != 0 } #[inline] fn is_weak(&self) -> bool { self.symbol.storage_class() == pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL } #[inline] fn scope(&self) -> SymbolScope { match self.symbol.storage_class() { pe::IMAGE_SYM_CLASS_EXTERNAL | pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL => { // TODO: determine if symbol is exported SymbolScope::Linkage } _ => SymbolScope::Compilation, } } #[inline] fn is_global(&self) -> bool { match self.symbol.storage_class() { pe::IMAGE_SYM_CLASS_EXTERNAL | pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL => true, _ => false, } } #[inline] fn is_local(&self) -> bool { !self.is_global() } fn flags(&self) -> SymbolFlags { if self.symbol.has_aux_section() { if let Ok(aux) = self.file.symbols.aux_section(self.index) { let number = if Coff::is_type_bigobj() { u32::from(aux.number.get(LE)) | (u32::from(aux.high_number.get(LE)) << 16) } else { u32::from(aux.number.get(LE)) }; return SymbolFlags::CoffSection { selection: aux.selection, associative_section: if number == 0 { None } else { Some(SectionIndex(number as usize)) }, }; } } SymbolFlags::None } } /// A trait for generic access to [`pe::ImageSymbol`] and [`pe::ImageSymbolEx`]. #[allow(missing_docs)] pub trait ImageSymbol: Debug + Pod { fn raw_name(&self) -> &[u8; 8]; fn value(&self) -> u32; fn section_number(&self) -> i32; fn typ(&self) -> u16; fn storage_class(&self) -> u8; fn number_of_aux_symbols(&self) -> u8; /// Parse a COFF symbol name. /// /// `strings` must be the string table used for symbol names. fn name<'data, R: ReadRef<'data>>( &'data self, strings: StringTable<'data, R>, ) -> Result<&'data [u8]> { let name = self.raw_name(); if name[0] == 0 { // If the name starts with 0 then the last 4 bytes are a string table offset. let offset = u32::from_le_bytes(name[4..8].try_into().unwrap()); strings .get(offset) .read_error("Invalid COFF symbol name offset") } else { // The name is inline and padded with nulls. Ok(match memchr::memchr(b'\0', name) { Some(end) => &name[..end], None => &name[..], }) } } /// Return the symbol address. /// /// This takes into account the image base and the section address, /// and only returns an address for symbols that have an address. fn address(&self, image_base: u64, sections: &SectionTable<'_>) -> Result> { // Only return an address for storage classes that we know use an address. match self.storage_class() { pe::IMAGE_SYM_CLASS_STATIC | pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL | pe::IMAGE_SYM_CLASS_LABEL | pe::IMAGE_SYM_CLASS_EXTERNAL => {} _ => return Ok(None), } let Some(section_index) = self.section() else { return Ok(None); }; let section = sections.section(section_index)?; let virtual_address = u64::from(section.virtual_address.get(LE)); let value = u64::from(self.value()); Ok(Some(image_base + virtual_address + value)) } /// Return the section index for the symbol. fn section(&self) -> Option { let section_number = self.section_number(); if section_number > 0 { Some(SectionIndex(section_number as usize)) } else { None } } /// Return true if the symbol is a definition of a function or data object. fn is_definition(&self) -> bool { if self.section_number() <= 0 { return false; } match self.storage_class() { pe::IMAGE_SYM_CLASS_STATIC => !self.has_aux_section(), pe::IMAGE_SYM_CLASS_EXTERNAL | pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL => true, _ => false, } } /// Return true if the symbol has an auxiliary file name. fn has_aux_file_name(&self) -> bool { self.number_of_aux_symbols() > 0 && self.storage_class() == pe::IMAGE_SYM_CLASS_FILE } /// Return true if the symbol has an auxiliary function symbol. fn has_aux_function(&self) -> bool { self.number_of_aux_symbols() > 0 && self.derived_type() == pe::IMAGE_SYM_DTYPE_FUNCTION } /// Return true if the symbol has an auxiliary section symbol. fn has_aux_section(&self) -> bool { self.number_of_aux_symbols() > 0 && self.storage_class() == pe::IMAGE_SYM_CLASS_STATIC && self.typ() == 0 } fn base_type(&self) -> u16 { self.typ() & pe::N_BTMASK } fn derived_type(&self) -> u16 { (self.typ() & pe::N_TMASK) >> pe::N_BTSHFT } } impl ImageSymbol for pe::ImageSymbol { fn raw_name(&self) -> &[u8; 8] { &self.name } fn value(&self) -> u32 { self.value.get(LE) } fn section_number(&self) -> i32 { let section_number = self.section_number.get(LE); if section_number >= pe::IMAGE_SYM_SECTION_MAX { (section_number as i16) as i32 } else { section_number as i32 } } fn typ(&self) -> u16 { self.typ.get(LE) } fn storage_class(&self) -> u8 { self.storage_class } fn number_of_aux_symbols(&self) -> u8 { self.number_of_aux_symbols } } impl ImageSymbol for pe::ImageSymbolEx { fn raw_name(&self) -> &[u8; 8] { &self.name } fn value(&self) -> u32 { self.value.get(LE) } fn section_number(&self) -> i32 { self.section_number.get(LE) } fn typ(&self) -> u16 { self.typ.get(LE) } fn storage_class(&self) -> u8 { self.storage_class } fn number_of_aux_symbols(&self) -> u8 { self.number_of_aux_symbols } } object-0.36.5/src/read/elf/attributes.rs000064400000000000000000000237711046102023000162030ustar 00000000000000use core::convert::TryInto; use crate::elf; use crate::endian; use crate::read::{Bytes, Error, ReadError, Result}; use super::FileHeader; /// An ELF attributes section. /// /// This may be a GNU attributes section, or an architecture specific attributes section. /// /// An attributes section contains a series of [`AttributesSubsection`]. /// /// Returned by [`SectionHeader::attributes`](super::SectionHeader::attributes) /// and [`SectionHeader::gnu_attributes`](super::SectionHeader::gnu_attributes). #[derive(Debug, Clone)] pub struct AttributesSection<'data, Elf: FileHeader> { endian: Elf::Endian, version: u8, data: Bytes<'data>, } impl<'data, Elf: FileHeader> AttributesSection<'data, Elf> { /// Parse an ELF attributes section given the section data. pub fn new(endian: Elf::Endian, data: &'data [u8]) -> Result { let mut data = Bytes(data); // Skip the version field that is one byte long. // If the section is empty then the version doesn't matter. let version = data.read::().cloned().unwrap_or(b'A'); Ok(AttributesSection { endian, version, data, }) } /// Return the version of the attributes section. pub fn version(&self) -> u8 { self.version } /// Return an iterator over the subsections. pub fn subsections(&self) -> Result> { // There is currently only one format version. if self.version != b'A' { return Err(Error("Unsupported ELF attributes section version")); } Ok(AttributesSubsectionIterator { endian: self.endian, data: self.data, }) } } /// An iterator for the subsections in an [`AttributesSection`]. #[derive(Debug, Clone)] pub struct AttributesSubsectionIterator<'data, Elf: FileHeader> { endian: Elf::Endian, data: Bytes<'data>, } impl<'data, Elf: FileHeader> AttributesSubsectionIterator<'data, Elf> { /// Return the next subsection. pub fn next(&mut self) -> Result>> { if self.data.is_empty() { return Ok(None); } let result = self.parse().map(Some); if result.is_err() { self.data = Bytes(&[]); } result } fn parse(&mut self) -> Result> { // First read the subsection length. let mut data = self.data; let length = data .read::>() .read_error("ELF attributes section is too short")? .get(self.endian); // Now read the entire subsection, updating self.data. let mut data = self .data .read_bytes(length as usize) .read_error("Invalid ELF attributes subsection length")?; // Skip the subsection length field. data.skip(4) .read_error("Invalid ELF attributes subsection length")?; // TODO: errors here should not prevent reading the next subsection. let vendor = data .read_string() .read_error("Invalid ELF attributes vendor")?; Ok(AttributesSubsection { endian: self.endian, length, vendor, data, }) } } impl<'data, Elf: FileHeader> Iterator for AttributesSubsectionIterator<'data, Elf> { type Item = Result>; fn next(&mut self) -> Option { self.next().transpose() } } /// A subsection in an [`AttributesSection`]. /// /// A subsection is identified by a vendor name. It contains a series of /// [`AttributesSubsubsection`]. #[derive(Debug, Clone)] pub struct AttributesSubsection<'data, Elf: FileHeader> { endian: Elf::Endian, length: u32, vendor: &'data [u8], data: Bytes<'data>, } impl<'data, Elf: FileHeader> AttributesSubsection<'data, Elf> { /// Return the length of the attributes subsection. pub fn length(&self) -> u32 { self.length } /// Return the vendor name of the attributes subsection. pub fn vendor(&self) -> &'data [u8] { self.vendor } /// Return an iterator over the sub-subsections. pub fn subsubsections(&self) -> AttributesSubsubsectionIterator<'data, Elf> { AttributesSubsubsectionIterator { endian: self.endian, data: self.data, } } } /// An iterator for the sub-subsections in an [`AttributesSubsection`]. #[derive(Debug, Clone)] pub struct AttributesSubsubsectionIterator<'data, Elf: FileHeader> { endian: Elf::Endian, data: Bytes<'data>, } impl<'data, Elf: FileHeader> AttributesSubsubsectionIterator<'data, Elf> { /// Return the next sub-subsection. pub fn next(&mut self) -> Result>> { if self.data.is_empty() { return Ok(None); } let result = self.parse().map(Some); if result.is_err() { self.data = Bytes(&[]); } result } fn parse(&mut self) -> Result> { // The format of a sub-section looks like this: // // * // | * 0 * // | * 0 * let mut data = self.data; let tag = *data .read::() .read_error("ELF attributes subsection is too short")?; let length = data .read::>() .read_error("ELF attributes subsection is too short")? .get(self.endian); // Now read the entire sub-subsection, updating self.data. let mut data = self .data .read_bytes(length as usize) .read_error("Invalid ELF attributes sub-subsection length")?; // Skip the tag and sub-subsection size field. data.skip(1 + 4) .read_error("Invalid ELF attributes sub-subsection length")?; // TODO: errors here should not prevent reading the next sub-subsection. let indices = if tag == elf::Tag_Section || tag == elf::Tag_Symbol { data.read_string() .map(Bytes) .read_error("Missing ELF attributes sub-subsection indices")? } else if tag == elf::Tag_File { Bytes(&[]) } else { return Err(Error("Unimplemented ELF attributes sub-subsection tag")); }; Ok(AttributesSubsubsection { tag, length, indices, data, }) } } impl<'data, Elf: FileHeader> Iterator for AttributesSubsubsectionIterator<'data, Elf> { type Item = Result>; fn next(&mut self) -> Option { self.next().transpose() } } /// A sub-subsection in an [`AttributesSubsection`]. /// /// A sub-subsection is identified by a tag. It contains an optional series of indices, /// followed by a series of attributes. #[derive(Debug, Clone)] pub struct AttributesSubsubsection<'data> { tag: u8, length: u32, indices: Bytes<'data>, data: Bytes<'data>, } impl<'data> AttributesSubsubsection<'data> { /// Return the tag of the attributes sub-subsection. pub fn tag(&self) -> u8 { self.tag } /// Return the length of the attributes sub-subsection. pub fn length(&self) -> u32 { self.length } /// Return the data containing the indices. pub fn indices_data(&self) -> &'data [u8] { self.indices.0 } /// Return the indices. /// /// This will be section indices if the tag is `Tag_Section`, /// or symbol indices if the tag is `Tag_Symbol`, /// and otherwise it will be empty. pub fn indices(&self) -> AttributeIndexIterator<'data> { AttributeIndexIterator { data: self.indices } } /// Return the data containing the attributes. pub fn attributes_data(&self) -> &'data [u8] { self.data.0 } /// Return a parser for the data containing the attributes. pub fn attributes(&self) -> AttributeReader<'data> { AttributeReader { data: self.data } } } /// An iterator over the indices in an [`AttributesSubsubsection`]. #[derive(Debug, Clone)] pub struct AttributeIndexIterator<'data> { data: Bytes<'data>, } impl<'data> AttributeIndexIterator<'data> { /// Parse the next index. pub fn next(&mut self) -> Result> { if self.data.is_empty() { return Ok(None); } let result = self.parse().map(Some); if result.is_err() { self.data = Bytes(&[]); } result } fn parse(&mut self) -> Result { let err = "Invalid ELF attribute index"; self.data .read_uleb128() .read_error(err)? .try_into() .map_err(|_| ()) .read_error(err) } } impl<'data> Iterator for AttributeIndexIterator<'data> { type Item = Result; fn next(&mut self) -> Option { self.next().transpose() } } /// A parser for the attributes in an [`AttributesSubsubsection`]. /// /// The parser relies on the caller to know the format of the data for each attribute tag. #[derive(Debug, Clone)] pub struct AttributeReader<'data> { data: Bytes<'data>, } impl<'data> AttributeReader<'data> { /// Parse a tag. pub fn read_tag(&mut self) -> Result> { if self.data.is_empty() { return Ok(None); } let err = "Invalid ELF attribute tag"; self.data.read_uleb128().read_error(err).map(Some) } /// Parse an integer value. pub fn read_integer(&mut self) -> Result { let err = "Invalid ELF attribute integer value"; self.data.read_uleb128().read_error(err) } /// Parse a string value. pub fn read_string(&mut self) -> Result<&'data [u8]> { let err = "Invalid ELF attribute string value"; self.data.read_string().read_error(err) } } object-0.36.5/src/read/elf/comdat.rs000064400000000000000000000133421046102023000152550ustar 00000000000000use core::fmt::Debug; use core::{iter, slice, str}; use crate::elf; use crate::endian::{Endianness, U32Bytes}; use crate::read::{self, ComdatKind, ObjectComdat, ReadError, ReadRef, SectionIndex, SymbolIndex}; use super::{ElfFile, FileHeader, SectionHeader, Sym}; /// An iterator for the COMDAT section groups in an [`ElfFile32`](super::ElfFile32). pub type ElfComdatIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfComdatIterator<'data, 'file, elf::FileHeader32, R>; /// An iterator for the COMDAT section groups in an [`ElfFile64`](super::ElfFile64). pub type ElfComdatIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfComdatIterator<'data, 'file, elf::FileHeader64, R>; /// An iterator for the COMDAT section groups in an [`ElfFile`]. #[derive(Debug)] pub struct ElfComdatIterator<'data, 'file, Elf, R = &'data [u8]> where Elf: FileHeader, R: ReadRef<'data>, { file: &'file ElfFile<'data, Elf, R>, iter: iter::Enumerate>, } impl<'data, 'file, Elf, R> ElfComdatIterator<'data, 'file, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { pub(super) fn new( file: &'file ElfFile<'data, Elf, R>, ) -> ElfComdatIterator<'data, 'file, Elf, R> { let mut iter = file.sections.iter().enumerate(); iter.next(); // Skip null section. ElfComdatIterator { file, iter } } } impl<'data, 'file, Elf, R> Iterator for ElfComdatIterator<'data, 'file, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { type Item = ElfComdat<'data, 'file, Elf, R>; fn next(&mut self) -> Option { for (_index, section) in self.iter.by_ref() { if let Some(comdat) = ElfComdat::parse(self.file, section) { return Some(comdat); } } None } } /// A COMDAT section group in an [`ElfFile32`](super::ElfFile32). pub type ElfComdat32<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfComdat<'data, 'file, elf::FileHeader32, R>; /// A COMDAT section group in an [`ElfFile64`](super::ElfFile64). pub type ElfComdat64<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfComdat<'data, 'file, elf::FileHeader64, R>; /// A COMDAT section group in an [`ElfFile`]. /// /// Most functionality is provided by the [`ObjectComdat`] trait implementation. #[derive(Debug)] pub struct ElfComdat<'data, 'file, Elf, R = &'data [u8]> where Elf: FileHeader, R: ReadRef<'data>, { file: &'file ElfFile<'data, Elf, R>, section: &'data Elf::SectionHeader, sections: &'data [U32Bytes], } impl<'data, 'file, Elf, R> ElfComdat<'data, 'file, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { fn parse( file: &'file ElfFile<'data, Elf, R>, section: &'data Elf::SectionHeader, ) -> Option> { let (flag, sections) = section.group(file.endian, file.data).ok()??; if flag != elf::GRP_COMDAT { return None; } Some(ElfComdat { file, section, sections, }) } /// Get the ELF file containing this COMDAT section group. pub fn elf_file(&self) -> &'file ElfFile<'data, Elf, R> { self.file } /// Get the raw ELF section header for the COMDAT section group. pub fn elf_section_header(&self) -> &'data Elf::SectionHeader { self.section } } impl<'data, 'file, Elf, R> read::private::Sealed for ElfComdat<'data, 'file, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { } impl<'data, 'file, Elf, R> ObjectComdat<'data> for ElfComdat<'data, 'file, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { type SectionIterator = ElfComdatSectionIterator<'data, 'file, Elf, R>; #[inline] fn kind(&self) -> ComdatKind { ComdatKind::Any } #[inline] fn symbol(&self) -> SymbolIndex { SymbolIndex(self.section.sh_info(self.file.endian) as usize) } fn name_bytes(&self) -> read::Result<&'data [u8]> { // FIXME: check sh_link let index = self.symbol(); let symbol = self.file.symbols.symbol(index)?; symbol.name(self.file.endian, self.file.symbols.strings()) } fn name(&self) -> read::Result<&'data str> { let name = self.name_bytes()?; str::from_utf8(name) .ok() .read_error("Non UTF-8 ELF COMDAT name") } fn sections(&self) -> Self::SectionIterator { ElfComdatSectionIterator { file: self.file, sections: self.sections.iter(), } } } /// An iterator for the sections in a COMDAT section group in an [`ElfFile32`](super::ElfFile32). pub type ElfComdatSectionIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfComdatSectionIterator<'data, 'file, elf::FileHeader32, R>; /// An iterator for the sections in a COMDAT section group in an [`ElfFile64`](super::ElfFile64). pub type ElfComdatSectionIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfComdatSectionIterator<'data, 'file, elf::FileHeader64, R>; /// An iterator for the sections in a COMDAT section group in an [`ElfFile`]. #[derive(Debug)] pub struct ElfComdatSectionIterator<'data, 'file, Elf, R = &'data [u8]> where Elf: FileHeader, R: ReadRef<'data>, { file: &'file ElfFile<'data, Elf, R>, sections: slice::Iter<'data, U32Bytes>, } impl<'data, 'file, Elf, R> Iterator for ElfComdatSectionIterator<'data, 'file, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { type Item = SectionIndex; fn next(&mut self) -> Option { let index = self.sections.next()?; Some(SectionIndex(index.get(self.file.endian) as usize)) } } object-0.36.5/src/read/elf/compression.rs000064400000000000000000000026421046102023000163500ustar 00000000000000use core::fmt::Debug; use crate::elf; use crate::endian; use crate::pod::Pod; /// A trait for generic access to [`elf::CompressionHeader32`] and [`elf::CompressionHeader64`]. #[allow(missing_docs)] pub trait CompressionHeader: Debug + Pod { type Word: Into; type Endian: endian::Endian; fn ch_type(&self, endian: Self::Endian) -> u32; fn ch_size(&self, endian: Self::Endian) -> Self::Word; fn ch_addralign(&self, endian: Self::Endian) -> Self::Word; } impl CompressionHeader for elf::CompressionHeader32 { type Word = u32; type Endian = Endian; #[inline] fn ch_type(&self, endian: Self::Endian) -> u32 { self.ch_type.get(endian) } #[inline] fn ch_size(&self, endian: Self::Endian) -> Self::Word { self.ch_size.get(endian) } #[inline] fn ch_addralign(&self, endian: Self::Endian) -> Self::Word { self.ch_addralign.get(endian) } } impl CompressionHeader for elf::CompressionHeader64 { type Word = u64; type Endian = Endian; #[inline] fn ch_type(&self, endian: Self::Endian) -> u32 { self.ch_type.get(endian) } #[inline] fn ch_size(&self, endian: Self::Endian) -> Self::Word { self.ch_size.get(endian) } #[inline] fn ch_addralign(&self, endian: Self::Endian) -> Self::Word { self.ch_addralign.get(endian) } } object-0.36.5/src/read/elf/dynamic.rs000064400000000000000000000062731046102023000154370ustar 00000000000000use core::convert::TryInto; use core::fmt::Debug; use crate::elf; use crate::endian; use crate::pod::Pod; use crate::read::{ReadError, Result, StringTable}; /// A trait for generic access to [`elf::Dyn32`] and [`elf::Dyn64`]. #[allow(missing_docs)] pub trait Dyn: Debug + Pod { type Word: Into; type Endian: endian::Endian; fn d_tag(&self, endian: Self::Endian) -> Self::Word; fn d_val(&self, endian: Self::Endian) -> Self::Word; /// Try to convert the tag to a `u32`. fn tag32(&self, endian: Self::Endian) -> Option { self.d_tag(endian).into().try_into().ok() } /// Try to convert the value to a `u32`. fn val32(&self, endian: Self::Endian) -> Option { self.d_val(endian).into().try_into().ok() } /// Return true if the value is an offset in the dynamic string table. fn is_string(&self, endian: Self::Endian) -> bool { if let Some(tag) = self.tag32(endian) { match tag { elf::DT_NEEDED | elf::DT_SONAME | elf::DT_RPATH | elf::DT_RUNPATH | elf::DT_AUXILIARY | elf::DT_FILTER => true, _ => false, } } else { false } } /// Use the value to get a string in a string table. /// /// Does not check for an appropriate tag. fn string<'data>( &self, endian: Self::Endian, strings: StringTable<'data>, ) -> Result<&'data [u8]> { self.val32(endian) .and_then(|val| strings.get(val).ok()) .read_error("Invalid ELF dyn string") } /// Return true if the value is an address. fn is_address(&self, endian: Self::Endian) -> bool { if let Some(tag) = self.tag32(endian) { match tag { elf::DT_PLTGOT | elf::DT_HASH | elf::DT_STRTAB | elf::DT_SYMTAB | elf::DT_RELA | elf::DT_INIT | elf::DT_FINI | elf::DT_SYMBOLIC | elf::DT_REL | elf::DT_DEBUG | elf::DT_JMPREL | elf::DT_FINI_ARRAY | elf::DT_INIT_ARRAY | elf::DT_PREINIT_ARRAY | elf::DT_SYMTAB_SHNDX | elf::DT_VERDEF | elf::DT_VERNEED | elf::DT_VERSYM | elf::DT_ADDRRNGLO..=elf::DT_ADDRRNGHI => true, _ => false, } } else { false } } } impl Dyn for elf::Dyn32 { type Word = u32; type Endian = Endian; #[inline] fn d_tag(&self, endian: Self::Endian) -> Self::Word { self.d_tag.get(endian) } #[inline] fn d_val(&self, endian: Self::Endian) -> Self::Word { self.d_val.get(endian) } } impl Dyn for elf::Dyn64 { type Word = u64; type Endian = Endian; #[inline] fn d_tag(&self, endian: Self::Endian) -> Self::Word { self.d_tag.get(endian) } #[inline] fn d_val(&self, endian: Self::Endian) -> Self::Word { self.d_val.get(endian) } } object-0.36.5/src/read/elf/file.rs000064400000000000000000000757531046102023000147430ustar 00000000000000use alloc::vec::Vec; use core::convert::TryInto; use core::fmt::Debug; use core::mem; use crate::elf; use crate::endian::{self, Endian, Endianness, U32}; use crate::pod::Pod; use crate::read::{ self, util, Architecture, ByteString, Bytes, Error, Export, FileFlags, Import, Object, ObjectKind, ReadError, ReadRef, SectionIndex, StringTable, SymbolIndex, }; use super::{ CompressionHeader, Dyn, ElfComdat, ElfComdatIterator, ElfDynamicRelocationIterator, ElfSection, ElfSectionIterator, ElfSegment, ElfSegmentIterator, ElfSymbol, ElfSymbolIterator, ElfSymbolTable, NoteHeader, ProgramHeader, Rel, Rela, RelocationSections, SectionHeader, SectionTable, Sym, SymbolTable, }; /// A 32-bit ELF object file. /// /// This is a file that starts with [`elf::FileHeader32`], and corresponds /// to [`crate::FileKind::Elf32`]. pub type ElfFile32<'data, Endian = Endianness, R = &'data [u8]> = ElfFile<'data, elf::FileHeader32, R>; /// A 64-bit ELF object file. /// /// This is a file that starts with [`elf::FileHeader64`], and corresponds /// to [`crate::FileKind::Elf64`]. pub type ElfFile64<'data, Endian = Endianness, R = &'data [u8]> = ElfFile<'data, elf::FileHeader64, R>; /// A partially parsed ELF file. /// /// Most functionality is provided by the [`Object`] trait implementation. #[derive(Debug)] pub struct ElfFile<'data, Elf, R = &'data [u8]> where Elf: FileHeader, R: ReadRef<'data>, { pub(super) endian: Elf::Endian, pub(super) data: R, pub(super) header: &'data Elf, pub(super) segments: &'data [Elf::ProgramHeader], pub(super) sections: SectionTable<'data, Elf, R>, pub(super) relocations: RelocationSections, pub(super) symbols: SymbolTable<'data, Elf, R>, pub(super) dynamic_symbols: SymbolTable<'data, Elf, R>, } impl<'data, Elf, R> ElfFile<'data, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { /// Parse the raw ELF file data. pub fn parse(data: R) -> read::Result { let header = Elf::parse(data)?; let endian = header.endian()?; let segments = header.program_headers(endian, data)?; let sections = header.sections(endian, data)?; let symbols = sections.symbols(endian, data, elf::SHT_SYMTAB)?; // TODO: get dynamic symbols from DT_SYMTAB if there are no sections let dynamic_symbols = sections.symbols(endian, data, elf::SHT_DYNSYM)?; // The API we provide requires a mapping from section to relocations, so build it now. let relocations = sections.relocation_sections(endian, symbols.section())?; Ok(ElfFile { endian, data, header, segments, sections, relocations, symbols, dynamic_symbols, }) } /// Returns the endianness. pub fn endian(&self) -> Elf::Endian { self.endian } /// Returns the raw data. pub fn data(&self) -> R { self.data } /// Returns the raw ELF file header. #[deprecated(note = "Use `elf_header` instead")] pub fn raw_header(&self) -> &'data Elf { self.header } /// Returns the raw ELF segments. #[deprecated(note = "Use `elf_program_headers` instead")] pub fn raw_segments(&self) -> &'data [Elf::ProgramHeader] { self.segments } /// Get the raw ELF file header. pub fn elf_header(&self) -> &'data Elf { self.header } /// Get the raw ELF program headers. /// /// Returns an empty slice if the file has no program headers. pub fn elf_program_headers(&self) -> &'data [Elf::ProgramHeader] { self.segments } /// Get the ELF section table. /// /// Returns an empty section table if the file has no section headers. pub fn elf_section_table(&self) -> &SectionTable<'data, Elf, R> { &self.sections } /// Get the ELF symbol table. /// /// Returns an empty symbol table if the file has no symbol table. pub fn elf_symbol_table(&self) -> &SymbolTable<'data, Elf, R> { &self.symbols } /// Get the ELF dynamic symbol table. /// /// Returns an empty symbol table if the file has no dynamic symbol table. pub fn elf_dynamic_symbol_table(&self) -> &SymbolTable<'data, Elf, R> { &self.dynamic_symbols } /// Get a mapping for linked relocation sections. pub fn elf_relocation_sections(&self) -> &RelocationSections { &self.relocations } fn raw_section_by_name<'file>( &'file self, section_name: &[u8], ) -> Option> { self.sections .section_by_name(self.endian, section_name) .map(|(index, section)| ElfSection { file: self, index, section, }) } #[cfg(feature = "compression")] fn zdebug_section_by_name<'file>( &'file self, section_name: &[u8], ) -> Option> { if !section_name.starts_with(b".debug_") { return None; } let mut name = Vec::with_capacity(section_name.len() + 1); name.extend_from_slice(b".zdebug_"); name.extend_from_slice(§ion_name[7..]); self.raw_section_by_name(&name) } #[cfg(not(feature = "compression"))] fn zdebug_section_by_name<'file>( &'file self, _section_name: &[u8], ) -> Option> { None } } impl<'data, Elf, R> read::private::Sealed for ElfFile<'data, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { } impl<'data, Elf, R> Object<'data> for ElfFile<'data, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { type Segment<'file> = ElfSegment<'data, 'file, Elf, R> where Self: 'file, 'data: 'file; type SegmentIterator<'file> = ElfSegmentIterator<'data, 'file, Elf, R> where Self: 'file, 'data: 'file; type Section<'file> = ElfSection<'data, 'file, Elf, R> where Self: 'file, 'data: 'file; type SectionIterator<'file> = ElfSectionIterator<'data, 'file, Elf, R> where Self: 'file, 'data: 'file; type Comdat<'file> = ElfComdat<'data, 'file, Elf, R> where Self: 'file, 'data: 'file; type ComdatIterator<'file> = ElfComdatIterator<'data, 'file, Elf, R> where Self: 'file, 'data: 'file; type Symbol<'file> = ElfSymbol<'data, 'file, Elf, R> where Self: 'file, 'data: 'file; type SymbolIterator<'file> = ElfSymbolIterator<'data, 'file, Elf, R> where Self: 'file, 'data: 'file; type SymbolTable<'file> = ElfSymbolTable<'data, 'file, Elf, R> where Self: 'file, 'data: 'file; type DynamicRelocationIterator<'file> = ElfDynamicRelocationIterator<'data, 'file, Elf, R> where Self: 'file, 'data: 'file; fn architecture(&self) -> Architecture { match ( self.header.e_machine(self.endian), self.header.is_class_64(), ) { (elf::EM_AARCH64, true) => Architecture::Aarch64, (elf::EM_AARCH64, false) => Architecture::Aarch64_Ilp32, (elf::EM_ARM, _) => Architecture::Arm, (elf::EM_AVR, _) => Architecture::Avr, (elf::EM_BPF, _) => Architecture::Bpf, (elf::EM_CSKY, _) => Architecture::Csky, (elf::EM_MCST_ELBRUS, false) => Architecture::E2K32, (elf::EM_MCST_ELBRUS, true) => Architecture::E2K64, (elf::EM_386, _) => Architecture::I386, (elf::EM_X86_64, false) => Architecture::X86_64_X32, (elf::EM_X86_64, true) => Architecture::X86_64, (elf::EM_HEXAGON, _) => Architecture::Hexagon, (elf::EM_LOONGARCH, true) => Architecture::LoongArch64, (elf::EM_MIPS, false) => Architecture::Mips, (elf::EM_MIPS, true) => Architecture::Mips64, (elf::EM_MSP430, _) => Architecture::Msp430, (elf::EM_PPC, _) => Architecture::PowerPc, (elf::EM_PPC64, _) => Architecture::PowerPc64, (elf::EM_RISCV, false) => Architecture::Riscv32, (elf::EM_RISCV, true) => Architecture::Riscv64, // This is either s390 or s390x, depending on the ELF class. // We only support the 64-bit variant s390x here. (elf::EM_S390, true) => Architecture::S390x, (elf::EM_SBF, _) => Architecture::Sbf, (elf::EM_SHARC, false) => Architecture::Sharc, (elf::EM_SPARC, false) => Architecture::Sparc, (elf::EM_SPARC32PLUS, false) => Architecture::Sparc32Plus, (elf::EM_SPARCV9, true) => Architecture::Sparc64, (elf::EM_XTENSA, false) => Architecture::Xtensa, _ => Architecture::Unknown, } } #[inline] fn is_little_endian(&self) -> bool { self.header.is_little_endian() } #[inline] fn is_64(&self) -> bool { self.header.is_class_64() } fn kind(&self) -> ObjectKind { match self.header.e_type(self.endian) { elf::ET_REL => ObjectKind::Relocatable, elf::ET_EXEC => ObjectKind::Executable, // TODO: check for `DF_1_PIE`? elf::ET_DYN => ObjectKind::Dynamic, elf::ET_CORE => ObjectKind::Core, _ => ObjectKind::Unknown, } } fn segments(&self) -> ElfSegmentIterator<'data, '_, Elf, R> { ElfSegmentIterator { file: self, iter: self.segments.iter(), } } fn section_by_name_bytes<'file>( &'file self, section_name: &[u8], ) -> Option> { self.raw_section_by_name(section_name) .or_else(|| self.zdebug_section_by_name(section_name)) } fn section_by_index(&self, index: SectionIndex) -> read::Result> { let section = self.sections.section(index)?; Ok(ElfSection { file: self, index, section, }) } fn sections(&self) -> ElfSectionIterator<'data, '_, Elf, R> { ElfSectionIterator::new(self) } fn comdats(&self) -> ElfComdatIterator<'data, '_, Elf, R> { ElfComdatIterator::new(self) } fn symbol_by_index(&self, index: SymbolIndex) -> read::Result> { let symbol = self.symbols.symbol(index)?; Ok(ElfSymbol { endian: self.endian, symbols: &self.symbols, index, symbol, }) } fn symbols(&self) -> ElfSymbolIterator<'data, '_, Elf, R> { ElfSymbolIterator::new(self.endian, &self.symbols) } fn symbol_table(&self) -> Option> { if self.symbols.is_empty() { return None; } Some(ElfSymbolTable { endian: self.endian, symbols: &self.symbols, }) } fn dynamic_symbols(&self) -> ElfSymbolIterator<'data, '_, Elf, R> { ElfSymbolIterator::new(self.endian, &self.dynamic_symbols) } fn dynamic_symbol_table(&self) -> Option> { if self.dynamic_symbols.is_empty() { return None; } Some(ElfSymbolTable { endian: self.endian, symbols: &self.dynamic_symbols, }) } fn dynamic_relocations<'file>( &'file self, ) -> Option> { Some(ElfDynamicRelocationIterator { section_index: SectionIndex(1), file: self, relocations: None, }) } fn imports(&self) -> read::Result>> { let versions = self.sections.versions(self.endian, self.data)?; let mut imports = Vec::new(); for (index, symbol) in self.dynamic_symbols.enumerate() { if symbol.is_undefined(self.endian) { let name = symbol.name(self.endian, self.dynamic_symbols.strings())?; if !name.is_empty() { let library = if let Some(svt) = versions.as_ref() { let vi = svt.version_index(self.endian, index); svt.version(vi)?.and_then(|v| v.file()) } else { None } .unwrap_or(&[]); imports.push(Import { name: ByteString(name), library: ByteString(library), }); } } } Ok(imports) } fn exports(&self) -> read::Result>> { let mut exports = Vec::new(); for symbol in self.dynamic_symbols.iter() { if symbol.is_definition(self.endian) { let name = symbol.name(self.endian, self.dynamic_symbols.strings())?; let address = symbol.st_value(self.endian).into(); exports.push(Export { name: ByteString(name), address, }); } } Ok(exports) } fn has_debug_symbols(&self) -> bool { for section in self.sections.iter() { if let Ok(name) = self.sections.section_name(self.endian, section) { if name == b".debug_info" || name == b".zdebug_info" { return true; } } } false } fn build_id(&self) -> read::Result> { let endian = self.endian; // Use section headers if present, otherwise use program headers. if !self.sections.is_empty() { for section in self.sections.iter() { if let Some(mut notes) = section.notes(endian, self.data)? { while let Some(note) = notes.next()? { if note.name() == elf::ELF_NOTE_GNU && note.n_type(endian) == elf::NT_GNU_BUILD_ID { return Ok(Some(note.desc())); } } } } } else { for segment in self.segments { if let Some(mut notes) = segment.notes(endian, self.data)? { while let Some(note) = notes.next()? { if note.name() == elf::ELF_NOTE_GNU && note.n_type(endian) == elf::NT_GNU_BUILD_ID { return Ok(Some(note.desc())); } } } } } Ok(None) } fn gnu_debuglink(&self) -> read::Result> { let section = match self.raw_section_by_name(b".gnu_debuglink") { Some(section) => section, None => return Ok(None), }; let data = section .section .data(self.endian, self.data) .read_error("Invalid ELF .gnu_debuglink section offset or size") .map(Bytes)?; let filename = data .read_string_at(0) .read_error("Missing ELF .gnu_debuglink filename")?; let crc_offset = util::align(filename.len() + 1, 4); let crc = data .read_at::>(crc_offset) .read_error("Missing ELF .gnu_debuglink crc")? .get(self.endian); Ok(Some((filename, crc))) } fn gnu_debugaltlink(&self) -> read::Result> { let section = match self.raw_section_by_name(b".gnu_debugaltlink") { Some(section) => section, None => return Ok(None), }; let mut data = section .section .data(self.endian, self.data) .read_error("Invalid ELF .gnu_debugaltlink section offset or size") .map(Bytes)?; let filename = data .read_string() .read_error("Missing ELF .gnu_debugaltlink filename")?; let build_id = data.0; Ok(Some((filename, build_id))) } fn relative_address_base(&self) -> u64 { 0 } fn entry(&self) -> u64 { self.header.e_entry(self.endian).into() } fn flags(&self) -> FileFlags { FileFlags::Elf { os_abi: self.header.e_ident().os_abi, abi_version: self.header.e_ident().abi_version, e_flags: self.header.e_flags(self.endian), } } } /// A trait for generic access to [`elf::FileHeader32`] and [`elf::FileHeader64`]. #[allow(missing_docs)] pub trait FileHeader: Debug + Pod { // Ideally this would be a `u64: From`, but can't express that. type Word: Into; type Sword: Into; type Endian: endian::Endian; type ProgramHeader: ProgramHeader; type SectionHeader: SectionHeader; type CompressionHeader: CompressionHeader; type NoteHeader: NoteHeader; type Dyn: Dyn; type Sym: Sym; type Rel: Rel; type Rela: Rela + From; /// Return true if this type is a 64-bit header. /// /// This is a property of the type, not a value in the header data. fn is_type_64(&self) -> bool; /// Return true if this type is a 64-bit header. /// /// This is a property of the type, not a value in the header data. /// /// This is the same as [`Self::is_type_64`], but is non-dispatchable. fn is_type_64_sized() -> bool where Self: Sized; fn e_ident(&self) -> &elf::Ident; fn e_type(&self, endian: Self::Endian) -> u16; fn e_machine(&self, endian: Self::Endian) -> u16; fn e_version(&self, endian: Self::Endian) -> u32; fn e_entry(&self, endian: Self::Endian) -> Self::Word; fn e_phoff(&self, endian: Self::Endian) -> Self::Word; fn e_shoff(&self, endian: Self::Endian) -> Self::Word; fn e_flags(&self, endian: Self::Endian) -> u32; fn e_ehsize(&self, endian: Self::Endian) -> u16; fn e_phentsize(&self, endian: Self::Endian) -> u16; fn e_phnum(&self, endian: Self::Endian) -> u16; fn e_shentsize(&self, endian: Self::Endian) -> u16; fn e_shnum(&self, endian: Self::Endian) -> u16; fn e_shstrndx(&self, endian: Self::Endian) -> u16; // Provided methods. /// Read the file header. /// /// Also checks that the ident field in the file header is a supported format. fn parse<'data, R: ReadRef<'data>>(data: R) -> read::Result<&'data Self> { let header = data .read_at::(0) .read_error("Invalid ELF header size or alignment")?; if !header.is_supported() { return Err(Error("Unsupported ELF header")); } // TODO: Check self.e_ehsize? Ok(header) } /// Check that the ident field in the file header is a supported format. /// /// This checks the magic number, version, class, and endianness. fn is_supported(&self) -> bool { let ident = self.e_ident(); // TODO: Check self.e_version too? Requires endian though. ident.magic == elf::ELFMAG && (self.is_type_64() || self.is_class_32()) && (!self.is_type_64() || self.is_class_64()) && (self.is_little_endian() || self.is_big_endian()) && ident.version == elf::EV_CURRENT } fn is_class_32(&self) -> bool { self.e_ident().class == elf::ELFCLASS32 } fn is_class_64(&self) -> bool { self.e_ident().class == elf::ELFCLASS64 } fn is_little_endian(&self) -> bool { self.e_ident().data == elf::ELFDATA2LSB } fn is_big_endian(&self) -> bool { self.e_ident().data == elf::ELFDATA2MSB } fn endian(&self) -> read::Result { Self::Endian::from_big_endian(self.is_big_endian()).read_error("Unsupported ELF endian") } /// Return the first section header, if present. /// /// Section 0 is a special case because getting the section headers normally /// requires `shnum`, but `shnum` may be in the first section header. fn section_0<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result> { let shoff: u64 = self.e_shoff(endian).into(); if shoff == 0 { // No section headers is ok. return Ok(None); } let shentsize = usize::from(self.e_shentsize(endian)); if shentsize != mem::size_of::() { // Section header size must match. return Err(Error("Invalid ELF section header entry size")); } data.read_at(shoff) .map(Some) .read_error("Invalid ELF section header offset or size") } /// Return the `e_phnum` field of the header. Handles extended values. /// /// Returns `Err` for invalid values. fn phnum<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result { let e_phnum = self.e_phnum(endian); if e_phnum < elf::PN_XNUM { Ok(e_phnum as usize) } else if let Some(section_0) = self.section_0(endian, data)? { Ok(section_0.sh_info(endian) as usize) } else { // Section 0 must exist if e_phnum overflows. Err(Error("Missing ELF section headers for e_phnum overflow")) } } /// Return the `e_shnum` field of the header. Handles extended values. /// /// Returns `Err` for invalid values. fn shnum<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result { let e_shnum = self.e_shnum(endian); if e_shnum > 0 { Ok(e_shnum as usize) } else if let Some(section_0) = self.section_0(endian, data)? { section_0 .sh_size(endian) .into() .try_into() .ok() .read_error("Invalid ELF extended e_shnum") } else { // No section headers is ok. Ok(0) } } /// Return the `e_shstrndx` field of the header. Handles extended values. /// /// Returns `Err` for invalid values (including if the index is 0). fn shstrndx<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result { let e_shstrndx = self.e_shstrndx(endian); let index = if e_shstrndx != elf::SHN_XINDEX { e_shstrndx.into() } else if let Some(section_0) = self.section_0(endian, data)? { section_0.sh_link(endian) } else { // Section 0 must exist if we're trying to read e_shstrndx. return Err(Error("Missing ELF section headers for e_shstrndx overflow")); }; if index == 0 { return Err(Error("Missing ELF e_shstrndx")); } Ok(index) } /// Return the slice of program headers. /// /// Returns `Ok(&[])` if there are no program headers. /// Returns `Err` for invalid values. fn program_headers<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result<&'data [Self::ProgramHeader]> { let phoff: u64 = self.e_phoff(endian).into(); if phoff == 0 { // No program headers is ok. return Ok(&[]); } let phnum = self.phnum(endian, data)?; if phnum == 0 { // No program headers is ok. return Ok(&[]); } let phentsize = self.e_phentsize(endian) as usize; if phentsize != mem::size_of::() { // Program header size must match. return Err(Error("Invalid ELF program header entry size")); } data.read_slice_at(phoff, phnum) .read_error("Invalid ELF program header size or alignment") } /// Return the slice of section headers. /// /// Returns `Ok(&[])` if there are no section headers. /// Returns `Err` for invalid values. fn section_headers<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result<&'data [Self::SectionHeader]> { let shoff: u64 = self.e_shoff(endian).into(); if shoff == 0 { // No section headers is ok. return Ok(&[]); } let shnum = self.shnum(endian, data)?; if shnum == 0 { // No section headers is ok. return Ok(&[]); } let shentsize = usize::from(self.e_shentsize(endian)); if shentsize != mem::size_of::() { // Section header size must match. return Err(Error("Invalid ELF section header entry size")); } data.read_slice_at(shoff, shnum) .read_error("Invalid ELF section header offset/size/alignment") } /// Get the section index of the section header string table. /// /// Returns `Err` for invalid values (including if the index is 0). fn section_strings_index<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result { self.shstrndx(endian, data) .map(|index| SectionIndex(index as usize)) } /// Return the string table for the section headers. fn section_strings<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, sections: &[Self::SectionHeader], ) -> read::Result> { if sections.is_empty() { return Ok(StringTable::default()); } let index = self.section_strings_index(endian, data)?; let shstrtab = sections.get(index.0).read_error("Invalid ELF e_shstrndx")?; let strings = if let Some((shstrtab_offset, shstrtab_size)) = shstrtab.file_range(endian) { let shstrtab_end = shstrtab_offset .checked_add(shstrtab_size) .read_error("Invalid ELF shstrtab size")?; StringTable::new(data, shstrtab_offset, shstrtab_end) } else { StringTable::default() }; Ok(strings) } /// Return the section table. fn sections<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result> { let sections = self.section_headers(endian, data)?; let strings = self.section_strings(endian, data, sections)?; Ok(SectionTable::new(sections, strings)) } /// Returns whether this is a mips64el elf file. fn is_mips64el(&self, endian: Self::Endian) -> bool { self.is_class_64() && self.is_little_endian() && self.e_machine(endian) == elf::EM_MIPS } } impl FileHeader for elf::FileHeader32 { type Word = u32; type Sword = i32; type Endian = Endian; type ProgramHeader = elf::ProgramHeader32; type SectionHeader = elf::SectionHeader32; type CompressionHeader = elf::CompressionHeader32; type NoteHeader = elf::NoteHeader32; type Dyn = elf::Dyn32; type Sym = elf::Sym32; type Rel = elf::Rel32; type Rela = elf::Rela32; #[inline] fn is_type_64(&self) -> bool { false } #[inline] fn is_type_64_sized() -> bool where Self: Sized, { false } #[inline] fn e_ident(&self) -> &elf::Ident { &self.e_ident } #[inline] fn e_type(&self, endian: Self::Endian) -> u16 { self.e_type.get(endian) } #[inline] fn e_machine(&self, endian: Self::Endian) -> u16 { self.e_machine.get(endian) } #[inline] fn e_version(&self, endian: Self::Endian) -> u32 { self.e_version.get(endian) } #[inline] fn e_entry(&self, endian: Self::Endian) -> Self::Word { self.e_entry.get(endian) } #[inline] fn e_phoff(&self, endian: Self::Endian) -> Self::Word { self.e_phoff.get(endian) } #[inline] fn e_shoff(&self, endian: Self::Endian) -> Self::Word { self.e_shoff.get(endian) } #[inline] fn e_flags(&self, endian: Self::Endian) -> u32 { self.e_flags.get(endian) } #[inline] fn e_ehsize(&self, endian: Self::Endian) -> u16 { self.e_ehsize.get(endian) } #[inline] fn e_phentsize(&self, endian: Self::Endian) -> u16 { self.e_phentsize.get(endian) } #[inline] fn e_phnum(&self, endian: Self::Endian) -> u16 { self.e_phnum.get(endian) } #[inline] fn e_shentsize(&self, endian: Self::Endian) -> u16 { self.e_shentsize.get(endian) } #[inline] fn e_shnum(&self, endian: Self::Endian) -> u16 { self.e_shnum.get(endian) } #[inline] fn e_shstrndx(&self, endian: Self::Endian) -> u16 { self.e_shstrndx.get(endian) } } impl FileHeader for elf::FileHeader64 { type Word = u64; type Sword = i64; type Endian = Endian; type ProgramHeader = elf::ProgramHeader64; type SectionHeader = elf::SectionHeader64; type CompressionHeader = elf::CompressionHeader64; type NoteHeader = elf::NoteHeader32; type Dyn = elf::Dyn64; type Sym = elf::Sym64; type Rel = elf::Rel64; type Rela = elf::Rela64; #[inline] fn is_type_64(&self) -> bool { true } #[inline] fn is_type_64_sized() -> bool where Self: Sized, { true } #[inline] fn e_ident(&self) -> &elf::Ident { &self.e_ident } #[inline] fn e_type(&self, endian: Self::Endian) -> u16 { self.e_type.get(endian) } #[inline] fn e_machine(&self, endian: Self::Endian) -> u16 { self.e_machine.get(endian) } #[inline] fn e_version(&self, endian: Self::Endian) -> u32 { self.e_version.get(endian) } #[inline] fn e_entry(&self, endian: Self::Endian) -> Self::Word { self.e_entry.get(endian) } #[inline] fn e_phoff(&self, endian: Self::Endian) -> Self::Word { self.e_phoff.get(endian) } #[inline] fn e_shoff(&self, endian: Self::Endian) -> Self::Word { self.e_shoff.get(endian) } #[inline] fn e_flags(&self, endian: Self::Endian) -> u32 { self.e_flags.get(endian) } #[inline] fn e_ehsize(&self, endian: Self::Endian) -> u16 { self.e_ehsize.get(endian) } #[inline] fn e_phentsize(&self, endian: Self::Endian) -> u16 { self.e_phentsize.get(endian) } #[inline] fn e_phnum(&self, endian: Self::Endian) -> u16 { self.e_phnum.get(endian) } #[inline] fn e_shentsize(&self, endian: Self::Endian) -> u16 { self.e_shentsize.get(endian) } #[inline] fn e_shnum(&self, endian: Self::Endian) -> u16 { self.e_shnum.get(endian) } #[inline] fn e_shstrndx(&self, endian: Self::Endian) -> u16 { self.e_shstrndx.get(endian) } } object-0.36.5/src/read/elf/hash.rs000064400000000000000000000202231046102023000147250ustar 00000000000000use core::mem; use crate::elf; use crate::endian::{U32, U64}; use crate::read::{ReadError, ReadRef, Result, SymbolIndex}; use super::{FileHeader, Sym, SymbolTable, Version, VersionTable}; /// A SysV symbol hash table in an ELF file. /// /// Returned by [`SectionHeader::hash`](super::SectionHeader::hash). #[derive(Debug)] pub struct HashTable<'data, Elf: FileHeader> { buckets: &'data [U32], chains: &'data [U32], } impl<'data, Elf: FileHeader> HashTable<'data, Elf> { /// Parse a SysV hash table. /// /// `data` should be from an [`elf::SHT_HASH`] section, or from a /// segment pointed to via the [`elf::DT_HASH`] entry. /// /// The header is read at offset 0 in the given `data`. pub fn parse(endian: Elf::Endian, data: &'data [u8]) -> Result { let mut offset = 0; let header = data .read::>(&mut offset) .read_error("Invalid hash header")?; let buckets = data .read_slice(&mut offset, header.bucket_count.get(endian) as usize) .read_error("Invalid hash buckets")?; let chains = data .read_slice(&mut offset, header.chain_count.get(endian) as usize) .read_error("Invalid hash chains")?; Ok(HashTable { buckets, chains }) } /// Return the symbol table length. pub fn symbol_table_length(&self) -> u32 { self.chains.len() as u32 } fn bucket(&self, endian: Elf::Endian, hash: u32) -> SymbolIndex { SymbolIndex(self.buckets[(hash as usize) % self.buckets.len()].get(endian) as usize) } fn chain(&self, endian: Elf::Endian, index: SymbolIndex) -> SymbolIndex { SymbolIndex(self.chains[index.0].get(endian) as usize) } /// Use the hash table to find the symbol table entry with the given name, hash and version. pub fn find>( &self, endian: Elf::Endian, name: &[u8], hash: u32, version: Option<&Version<'_>>, symbols: &SymbolTable<'data, Elf, R>, versions: &VersionTable<'data, Elf>, ) -> Option<(SymbolIndex, &'data Elf::Sym)> { // Get the chain start from the bucket for this hash. let mut index = self.bucket(endian, hash); // Avoid infinite loop. let mut i = 0; let strings = symbols.strings(); while index != SymbolIndex(0) && i < self.chains.len() { if let Ok(symbol) = symbols.symbol(index) { if symbol.name(endian, strings) == Ok(name) && versions.matches(endian, index, version) { return Some((index, symbol)); } } index = self.chain(endian, index); i += 1; } None } } /// A GNU symbol hash table in an ELF file. /// /// Returned by [`SectionHeader::gnu_hash`](super::SectionHeader::gnu_hash). #[derive(Debug)] pub struct GnuHashTable<'data, Elf: FileHeader> { symbol_base: u32, bloom_shift: u32, bloom_filters: &'data [u8], buckets: &'data [U32], values: &'data [U32], } impl<'data, Elf: FileHeader> GnuHashTable<'data, Elf> { /// Parse a GNU hash table. /// /// `data` should be from an [`elf::SHT_GNU_HASH`] section, or from a /// segment pointed to via the [`elf::DT_GNU_HASH`] entry. /// /// The header is read at offset 0 in the given `data`. /// /// The header does not contain a length field, and so all of `data` /// will be used as the hash table values. It does not matter if this /// is longer than needed, and this will often the case when accessing /// the hash table via the [`elf::DT_GNU_HASH`] entry. pub fn parse(endian: Elf::Endian, data: &'data [u8]) -> Result { let mut offset = 0; let header = data .read::>(&mut offset) .read_error("Invalid GNU hash header")?; let bloom_len = u64::from(header.bloom_count.get(endian)) * mem::size_of::() as u64; let bloom_filters = data .read_bytes(&mut offset, bloom_len) .read_error("Invalid GNU hash bloom filters")?; let buckets = data .read_slice(&mut offset, header.bucket_count.get(endian) as usize) .read_error("Invalid GNU hash buckets")?; let chain_count = (data.len() - offset as usize) / 4; let values = data .read_slice(&mut offset, chain_count) .read_error("Invalid GNU hash values")?; Ok(GnuHashTable { symbol_base: header.symbol_base.get(endian), bloom_shift: header.bloom_shift.get(endian), bloom_filters, buckets, values, }) } /// Return the symbol table index of the first symbol in the hash table. pub fn symbol_base(&self) -> u32 { self.symbol_base } /// Determine the symbol table length by finding the last entry in the hash table. /// /// Returns `None` if the hash table is empty or invalid. pub fn symbol_table_length(&self, endian: Elf::Endian) -> Option { // Ensure we find a non-empty bucket. if self.symbol_base == 0 { return None; } // Find the highest chain index in a bucket. let mut max_symbol = 0; for bucket in self.buckets { let bucket = bucket.get(endian); if max_symbol < bucket { max_symbol = bucket; } } // Find the end of the chain. for value in self .values .get(max_symbol.checked_sub(self.symbol_base)? as usize..)? { max_symbol += 1; if value.get(endian) & 1 != 0 { return Some(max_symbol); } } None } fn bucket(&self, endian: Elf::Endian, hash: u32) -> SymbolIndex { SymbolIndex(self.buckets[(hash as usize) % self.buckets.len()].get(endian) as usize) } /// Use the hash table to find the symbol table entry with the given name, hash, and version. pub fn find>( &self, endian: Elf::Endian, name: &[u8], hash: u32, version: Option<&Version<'_>>, symbols: &SymbolTable<'data, Elf, R>, versions: &VersionTable<'data, Elf>, ) -> Option<(SymbolIndex, &'data Elf::Sym)> { let word_bits = mem::size_of::() as u32 * 8; // Test against bloom filter. let bloom_count = self.bloom_filters.len() / mem::size_of::(); let offset = ((hash / word_bits) & (bloom_count as u32 - 1)) * mem::size_of::() as u32; let filter = if word_bits == 64 { self.bloom_filters .read_at::>(offset.into()) .ok()? .get(endian) } else { self.bloom_filters .read_at::>(offset.into()) .ok()? .get(endian) .into() }; if filter & (1 << (hash % word_bits)) == 0 { return None; } if filter & (1 << ((hash >> self.bloom_shift) % word_bits)) == 0 { return None; } // Get the chain start from the bucket for this hash. let mut index = self.bucket(endian, hash); if index == SymbolIndex(0) { return None; } // Test symbols in the chain. let strings = symbols.strings(); let symbols = symbols.symbols().get(index.0..)?; let values = self .values .get(index.0.checked_sub(self.symbol_base as usize)?..)?; for (symbol, value) in symbols.iter().zip(values.iter()) { let value = value.get(endian); if value | 1 == hash | 1 { if symbol.name(endian, strings) == Ok(name) && versions.matches(endian, index, version) { return Some((index, symbol)); } } if value & 1 != 0 { break; } index.0 += 1; } None } } object-0.36.5/src/read/elf/mod.rs000064400000000000000000000034761046102023000145740ustar 00000000000000//! Support for reading ELF files. //! //! Traits are used to abstract over the difference between 32-bit and 64-bit ELF. //! The primary trait for this is [`FileHeader`]. //! //! ## High level API //! //! [`ElfFile`] implements the [`Object`](crate::read::Object) trait for ELF files. //! [`ElfFile`] is parameterised by [`FileHeader`] to allow reading both 32-bit and //! 64-bit ELF. There are type aliases for these parameters ([`ElfFile32`] and //! [`ElfFile64`]). //! //! ## Low level API //! //! The [`FileHeader`] trait can be directly used to parse both [`elf::FileHeader32`] //! and [`elf::FileHeader64`]. //! //! ### Example for low level API //! ```no_run //! use object::elf; //! use object::read::elf::{FileHeader, Sym}; //! use std::error::Error; //! use std::fs; //! //! /// Reads a file and displays the name of each symbol. //! fn main() -> Result<(), Box> { //! # #[cfg(feature = "std")] { //! let data = fs::read("path/to/binary")?; //! let elf = elf::FileHeader64::::parse(&*data)?; //! let endian = elf.endian()?; //! let sections = elf.sections(endian, &*data)?; //! let symbols = sections.symbols(endian, &*data, elf::SHT_SYMTAB)?; //! for symbol in symbols.iter() { //! let name = symbol.name(endian, symbols.strings())?; //! println!("{}", String::from_utf8_lossy(name)); //! } //! # } //! Ok(()) //! } //! ``` #[cfg(doc)] use crate::elf; mod file; pub use file::*; mod segment; pub use segment::*; mod section; pub use section::*; mod symbol; pub use symbol::*; mod relocation; pub use relocation::*; mod comdat; pub use comdat::*; mod dynamic; pub use dynamic::*; mod compression; pub use compression::*; mod note; pub use note::*; mod hash; pub use hash::*; mod version; pub use version::*; mod attributes; pub use attributes::*; object-0.36.5/src/read/elf/note.rs000064400000000000000000000210321046102023000147460ustar 00000000000000use core::fmt::Debug; use core::mem; use crate::elf; use crate::endian::{self, U32}; use crate::pod::Pod; use crate::read::util; use crate::read::{self, Bytes, Error, ReadError}; use super::FileHeader; /// An iterator over the notes in an ELF section or segment. /// /// Returned [`ProgramHeader::notes`](super::ProgramHeader::notes) /// and [`SectionHeader::notes`](super::SectionHeader::notes). #[derive(Debug)] pub struct NoteIterator<'data, Elf> where Elf: FileHeader, { endian: Elf::Endian, align: usize, data: Bytes<'data>, } impl<'data, Elf> NoteIterator<'data, Elf> where Elf: FileHeader, { /// An iterator over the notes in an ELF section or segment. /// /// `align` should be from the `p_align` field of the segment, /// or the `sh_addralign` field of the section. Supported values are /// either 4 or 8, but values less than 4 are treated as 4. /// This matches the behaviour of binutils. /// /// Returns `Err` if `align` is invalid. pub fn new(endian: Elf::Endian, align: Elf::Word, data: &'data [u8]) -> read::Result { let align = match align.into() { 0u64..=4 => 4, 8 => 8, _ => return Err(Error("Invalid ELF note alignment")), }; // TODO: check data alignment? Ok(NoteIterator { endian, align, data: Bytes(data), }) } /// Returns the next note. pub fn next(&mut self) -> read::Result>> { if self.data.is_empty() { return Ok(None); } let result = self.parse().map(Some); if result.is_err() { self.data = Bytes(&[]); } result } fn parse(&mut self) -> read::Result> { let header = self .data .read_at::(0) .read_error("ELF note is too short")?; // The name has no alignment requirement. let offset = mem::size_of::(); let namesz = header.n_namesz(self.endian) as usize; let name = self .data .read_bytes_at(offset, namesz) .read_error("Invalid ELF note namesz")? .0; // The descriptor must be aligned. let offset = util::align(offset + namesz, self.align); let descsz = header.n_descsz(self.endian) as usize; let desc = self .data .read_bytes_at(offset, descsz) .read_error("Invalid ELF note descsz")? .0; // The next note (if any) must be aligned. let offset = util::align(offset + descsz, self.align); if self.data.skip(offset).is_err() { self.data = Bytes(&[]); } Ok(Note { header, name, desc }) } } impl<'data, Elf: FileHeader> Iterator for NoteIterator<'data, Elf> { type Item = read::Result>; fn next(&mut self) -> Option { self.next().transpose() } } /// A parsed [`NoteHeader`]. #[derive(Debug)] pub struct Note<'data, Elf> where Elf: FileHeader, { header: &'data Elf::NoteHeader, name: &'data [u8], desc: &'data [u8], } impl<'data, Elf: FileHeader> Note<'data, Elf> { /// Return the `n_type` field of the `NoteHeader`. /// /// The meaning of this field is determined by `name`. pub fn n_type(&self, endian: Elf::Endian) -> u32 { self.header.n_type(endian) } /// Return the `n_namesz` field of the `NoteHeader`. pub fn n_namesz(&self, endian: Elf::Endian) -> u32 { self.header.n_namesz(endian) } /// Return the `n_descsz` field of the `NoteHeader`. pub fn n_descsz(&self, endian: Elf::Endian) -> u32 { self.header.n_descsz(endian) } /// Return the bytes for the name field following the `NoteHeader`. /// /// This field is usually a string including one or more trailing null bytes /// (but it is not required to be). /// /// The length of this field is given by `n_namesz`. pub fn name_bytes(&self) -> &'data [u8] { self.name } /// Return the bytes for the name field following the `NoteHeader`, /// excluding all trailing null bytes. pub fn name(&self) -> &'data [u8] { let mut name = self.name; while let [rest @ .., 0] = name { name = rest; } name } /// Return the bytes for the desc field following the `NoteHeader`. /// /// The length of this field is given by `n_descsz`. The meaning /// of this field is determined by `name` and `n_type`. pub fn desc(&self) -> &'data [u8] { self.desc } /// Return an iterator for properties if this note's type is [`elf::NT_GNU_PROPERTY_TYPE_0`]. pub fn gnu_properties( &self, endian: Elf::Endian, ) -> Option> { if self.name() != elf::ELF_NOTE_GNU || self.n_type(endian) != elf::NT_GNU_PROPERTY_TYPE_0 { return None; } // Use the ELF class instead of the section alignment. // This matches what other parsers do. let align = if Elf::is_type_64_sized() { 8 } else { 4 }; Some(GnuPropertyIterator { endian, align, data: Bytes(self.desc), }) } } /// A trait for generic access to [`elf::NoteHeader32`] and [`elf::NoteHeader64`]. #[allow(missing_docs)] pub trait NoteHeader: Debug + Pod { type Endian: endian::Endian; fn n_namesz(&self, endian: Self::Endian) -> u32; fn n_descsz(&self, endian: Self::Endian) -> u32; fn n_type(&self, endian: Self::Endian) -> u32; } impl NoteHeader for elf::NoteHeader32 { type Endian = Endian; #[inline] fn n_namesz(&self, endian: Self::Endian) -> u32 { self.n_namesz.get(endian) } #[inline] fn n_descsz(&self, endian: Self::Endian) -> u32 { self.n_descsz.get(endian) } #[inline] fn n_type(&self, endian: Self::Endian) -> u32 { self.n_type.get(endian) } } impl NoteHeader for elf::NoteHeader64 { type Endian = Endian; #[inline] fn n_namesz(&self, endian: Self::Endian) -> u32 { self.n_namesz.get(endian) } #[inline] fn n_descsz(&self, endian: Self::Endian) -> u32 { self.n_descsz.get(endian) } #[inline] fn n_type(&self, endian: Self::Endian) -> u32 { self.n_type.get(endian) } } /// An iterator for the properties in a [`elf::NT_GNU_PROPERTY_TYPE_0`] note. /// /// Returned by [`Note::gnu_properties`]. #[derive(Debug)] pub struct GnuPropertyIterator<'data, Endian: endian::Endian> { endian: Endian, align: usize, data: Bytes<'data>, } impl<'data, Endian: endian::Endian> GnuPropertyIterator<'data, Endian> { /// Returns the next property. pub fn next(&mut self) -> read::Result>> { if self.data.is_empty() { return Ok(None); } let result = self.parse().map(Some); if result.is_err() { self.data = Bytes(&[]); } result } fn parse(&mut self) -> read::Result> { (|| -> Result<_, ()> { let pr_type = self.data.read_at::>(0)?.get(self.endian); let pr_datasz = self.data.read_at::>(4)?.get(self.endian) as usize; let pr_data = self.data.read_bytes_at(8, pr_datasz)?.0; self.data.skip(util::align(8 + pr_datasz, self.align))?; Ok(GnuProperty { pr_type, pr_data }) })() .read_error("Invalid ELF GNU property") } } impl<'data, Endian: endian::Endian> Iterator for GnuPropertyIterator<'data, Endian> { type Item = read::Result>; fn next(&mut self) -> Option { self.next().transpose() } } /// A property in a [`elf::NT_GNU_PROPERTY_TYPE_0`] note. #[derive(Debug)] pub struct GnuProperty<'data> { pr_type: u32, pr_data: &'data [u8], } impl<'data> GnuProperty<'data> { /// Return the property type. /// /// This is one of the `GNU_PROPERTY_*` constants. pub fn pr_type(&self) -> u32 { self.pr_type } /// Return the property data. pub fn pr_data(&self) -> &'data [u8] { self.pr_data } /// Parse the property data as an unsigned 32-bit integer. pub fn data_u32(&self, endian: E) -> read::Result { Bytes(self.pr_data) .read_at::>(0) .read_error("Invalid ELF GNU property data") .map(|val| val.get(endian)) } } object-0.36.5/src/read/elf/relocation.rs000064400000000000000000000527641046102023000161600ustar 00000000000000use alloc::fmt; use alloc::vec::Vec; use core::fmt::Debug; use core::slice; use crate::elf; use crate::endian::{self, Endianness}; use crate::pod::Pod; use crate::read::{ self, Error, ReadRef, Relocation, RelocationEncoding, RelocationFlags, RelocationKind, RelocationTarget, SectionIndex, SymbolIndex, }; use super::{ElfFile, FileHeader, SectionHeader, SectionTable}; /// A mapping from section index to associated relocation sections. #[derive(Debug, Default)] pub struct RelocationSections { relocations: Vec, } impl RelocationSections { /// Create a new mapping using the section table. /// /// Skips relocation sections that do not use the given symbol table section. pub fn parse<'data, Elf: FileHeader, R: ReadRef<'data>>( endian: Elf::Endian, sections: &SectionTable<'data, Elf, R>, symbol_section: SectionIndex, ) -> read::Result { let mut relocations = vec![0; sections.len()]; for (index, section) in sections.iter().enumerate().rev() { let sh_type = section.sh_type(endian); if sh_type == elf::SHT_REL || sh_type == elf::SHT_RELA { // The symbol indices used in relocations must be for the symbol table // we are expecting to use. let sh_link = section.link(endian); if sh_link != symbol_section { continue; } let sh_info = section.info_link(endian); if sh_info == SectionIndex(0) { // Skip dynamic relocations. continue; } if sh_info.0 >= relocations.len() { return Err(Error("Invalid ELF sh_info for relocation section")); } // We don't support relocations that apply to other relocation sections // because it interferes with the chaining of relocation sections below. let sh_info_type = sections.section(sh_info)?.sh_type(endian); if sh_info_type == elf::SHT_REL || sh_info_type == elf::SHT_RELA { return Err(Error("Unsupported ELF sh_info for relocation section")); } // Handle multiple relocation sections by chaining them. let next = relocations[sh_info.0]; relocations[sh_info.0] = index; relocations[index] = next; } } Ok(Self { relocations }) } /// Given a section index, return the section index of the associated relocation section. /// /// This may also be called with a relocation section index, and it will return the /// next associated relocation section. pub fn get(&self, index: SectionIndex) -> Option { self.relocations .get(index.0) .cloned() .filter(|x| *x != 0) .map(SectionIndex) } } pub(super) enum ElfRelaIterator<'data, Elf: FileHeader> { Rel(slice::Iter<'data, Elf::Rel>), Rela(slice::Iter<'data, Elf::Rela>), } impl<'data, Elf: FileHeader> ElfRelaIterator<'data, Elf> { fn is_rel(&self) -> bool { match self { ElfRelaIterator::Rel(_) => true, ElfRelaIterator::Rela(_) => false, } } } impl<'data, Elf: FileHeader> Iterator for ElfRelaIterator<'data, Elf> { type Item = Elf::Rela; fn next(&mut self) -> Option { match self { ElfRelaIterator::Rel(ref mut i) => i.next().cloned().map(Self::Item::from), ElfRelaIterator::Rela(ref mut i) => i.next().cloned(), } } } /// An iterator for the dynamic relocations in an [`ElfFile32`](super::ElfFile32). pub type ElfDynamicRelocationIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfDynamicRelocationIterator<'data, 'file, elf::FileHeader32, R>; /// An iterator for the dynamic relocations in an [`ElfFile64`](super::ElfFile64). pub type ElfDynamicRelocationIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfDynamicRelocationIterator<'data, 'file, elf::FileHeader64, R>; /// An iterator for the dynamic relocations in an [`ElfFile`]. pub struct ElfDynamicRelocationIterator<'data, 'file, Elf, R = &'data [u8]> where Elf: FileHeader, R: ReadRef<'data>, { /// The current relocation section index. pub(super) section_index: SectionIndex, pub(super) file: &'file ElfFile<'data, Elf, R>, pub(super) relocations: Option>, } impl<'data, 'file, Elf, R> Iterator for ElfDynamicRelocationIterator<'data, 'file, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { type Item = (u64, Relocation); fn next(&mut self) -> Option { let endian = self.file.endian; loop { if let Some(ref mut relocations) = self.relocations { if let Some(reloc) = relocations.next() { let relocation = parse_relocation(self.file.header, endian, reloc, relocations.is_rel()); return Some((reloc.r_offset(endian).into(), relocation)); } self.relocations = None; } let section = self.file.sections.section(self.section_index).ok()?; self.section_index.0 += 1; if section.link(endian) != self.file.dynamic_symbols.section() { continue; } match section.sh_type(endian) { elf::SHT_REL => { if let Ok(relocations) = section.data_as_array(endian, self.file.data) { self.relocations = Some(ElfRelaIterator::Rel(relocations.iter())); } } elf::SHT_RELA => { if let Ok(relocations) = section.data_as_array(endian, self.file.data) { self.relocations = Some(ElfRelaIterator::Rela(relocations.iter())); } } _ => {} } } } } impl<'data, 'file, Elf, R> fmt::Debug for ElfDynamicRelocationIterator<'data, 'file, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ElfDynamicRelocationIterator").finish() } } /// An iterator for the relocations for an [`ElfSection32`](super::ElfSection32). pub type ElfSectionRelocationIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfSectionRelocationIterator<'data, 'file, elf::FileHeader32, R>; /// An iterator for the relocations for an [`ElfSection64`](super::ElfSection64). pub type ElfSectionRelocationIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfSectionRelocationIterator<'data, 'file, elf::FileHeader64, R>; /// An iterator for the relocations for an [`ElfSection`](super::ElfSection). pub struct ElfSectionRelocationIterator<'data, 'file, Elf, R = &'data [u8]> where Elf: FileHeader, R: ReadRef<'data>, { /// The current pointer in the chain of relocation sections. pub(super) section_index: SectionIndex, pub(super) file: &'file ElfFile<'data, Elf, R>, pub(super) relocations: Option>, } impl<'data, 'file, Elf, R> Iterator for ElfSectionRelocationIterator<'data, 'file, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { type Item = (u64, Relocation); fn next(&mut self) -> Option { let endian = self.file.endian; loop { if let Some(ref mut relocations) = self.relocations { if let Some(reloc) = relocations.next() { let relocation = parse_relocation(self.file.header, endian, reloc, relocations.is_rel()); return Some((reloc.r_offset(endian).into(), relocation)); } self.relocations = None; } self.section_index = self.file.relocations.get(self.section_index)?; // The construction of RelocationSections ensures section_index is valid. let section = self.file.sections.section(self.section_index).unwrap(); match section.sh_type(endian) { elf::SHT_REL => { if let Ok(relocations) = section.data_as_array(endian, self.file.data) { self.relocations = Some(ElfRelaIterator::Rel(relocations.iter())); } } elf::SHT_RELA => { if let Ok(relocations) = section.data_as_array(endian, self.file.data) { self.relocations = Some(ElfRelaIterator::Rela(relocations.iter())); } } _ => {} } } } } impl<'data, 'file, Elf, R> fmt::Debug for ElfSectionRelocationIterator<'data, 'file, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ElfSectionRelocationIterator").finish() } } fn parse_relocation( header: &Elf, endian: Elf::Endian, reloc: Elf::Rela, implicit_addend: bool, ) -> Relocation { use RelocationEncoding as E; use RelocationKind as K; let is_mips64el = header.is_mips64el(endian); let r_type = reloc.r_type(endian, is_mips64el); let flags = RelocationFlags::Elf { r_type }; let g = E::Generic; let unknown = (K::Unknown, E::Generic, 0); let (kind, encoding, size) = match header.e_machine(endian) { elf::EM_AARCH64 => { if header.is_type_64() { match r_type { elf::R_AARCH64_ABS64 => (K::Absolute, g, 64), elf::R_AARCH64_ABS32 => (K::Absolute, g, 32), elf::R_AARCH64_ABS16 => (K::Absolute, g, 16), elf::R_AARCH64_PREL64 => (K::Relative, g, 64), elf::R_AARCH64_PREL32 => (K::Relative, g, 32), elf::R_AARCH64_PREL16 => (K::Relative, g, 16), elf::R_AARCH64_CALL26 => (K::PltRelative, E::AArch64Call, 26), _ => unknown, } } else { match r_type { elf::R_AARCH64_P32_ABS32 => (K::Absolute, g, 32), _ => unknown, } } } elf::EM_ARM => match r_type { elf::R_ARM_ABS32 => (K::Absolute, g, 32), _ => unknown, }, elf::EM_AVR => match r_type { elf::R_AVR_32 => (K::Absolute, g, 32), elf::R_AVR_16 => (K::Absolute, g, 16), _ => unknown, }, elf::EM_BPF => match r_type { elf::R_BPF_64_64 => (K::Absolute, g, 64), elf::R_BPF_64_32 => (K::Absolute, g, 32), _ => unknown, }, elf::EM_CSKY => match r_type { elf::R_CKCORE_ADDR32 => (K::Absolute, g, 32), elf::R_CKCORE_PCREL32 => (K::Relative, g, 32), _ => unknown, }, elf::EM_MCST_ELBRUS => match r_type { elf::R_E2K_32_ABS => (K::Absolute, g, 32), elf::R_E2K_64_ABS => (K::Absolute, g, 64), elf::R_E2K_64_ABS_LIT => (K::Absolute, E::E2KLit, 64), elf::R_E2K_DISP => (K::Relative, E::E2KDisp, 28), elf::R_E2K_GOT => (K::Got, g, 32), _ => unknown, }, elf::EM_386 => match r_type { elf::R_386_32 => (K::Absolute, g, 32), elf::R_386_PC32 => (K::Relative, g, 32), elf::R_386_GOT32 => (K::Got, g, 32), elf::R_386_PLT32 => (K::PltRelative, g, 32), elf::R_386_GOTOFF => (K::GotBaseOffset, g, 32), elf::R_386_GOTPC => (K::GotBaseRelative, g, 32), elf::R_386_16 => (K::Absolute, g, 16), elf::R_386_PC16 => (K::Relative, g, 16), elf::R_386_8 => (K::Absolute, g, 8), elf::R_386_PC8 => (K::Relative, g, 8), _ => unknown, }, elf::EM_X86_64 => match r_type { elf::R_X86_64_64 => (K::Absolute, g, 64), elf::R_X86_64_PC32 => (K::Relative, g, 32), elf::R_X86_64_GOT32 => (K::Got, g, 32), elf::R_X86_64_PLT32 => (K::PltRelative, g, 32), elf::R_X86_64_GOTPCREL => (K::GotRelative, g, 32), elf::R_X86_64_32 => (K::Absolute, g, 32), elf::R_X86_64_32S => (K::Absolute, E::X86Signed, 32), elf::R_X86_64_16 => (K::Absolute, g, 16), elf::R_X86_64_PC16 => (K::Relative, g, 16), elf::R_X86_64_8 => (K::Absolute, g, 8), elf::R_X86_64_PC8 => (K::Relative, g, 8), _ => unknown, }, elf::EM_HEXAGON => match r_type { elf::R_HEX_32 => (K::Absolute, g, 32), _ => unknown, }, elf::EM_LOONGARCH => match r_type { elf::R_LARCH_32 => (K::Absolute, g, 32), elf::R_LARCH_64 => (K::Absolute, g, 64), elf::R_LARCH_32_PCREL => (K::Relative, g, 32), elf::R_LARCH_64_PCREL => (K::Relative, g, 64), elf::R_LARCH_B16 => (K::Relative, E::LoongArchBranch, 16), elf::R_LARCH_B21 => (K::Relative, E::LoongArchBranch, 21), elf::R_LARCH_B26 => (K::Relative, E::LoongArchBranch, 26), _ => unknown, }, elf::EM_MIPS => match r_type { elf::R_MIPS_16 => (K::Absolute, g, 16), elf::R_MIPS_32 => (K::Absolute, g, 32), elf::R_MIPS_64 => (K::Absolute, g, 64), _ => unknown, }, elf::EM_MSP430 => match r_type { elf::R_MSP430_32 => (K::Absolute, g, 32), elf::R_MSP430_16_BYTE => (K::Absolute, g, 16), _ => unknown, }, elf::EM_PPC => match r_type { elf::R_PPC_ADDR32 => (K::Absolute, g, 32), _ => unknown, }, elf::EM_PPC64 => match r_type { elf::R_PPC64_ADDR32 => (K::Absolute, g, 32), elf::R_PPC64_ADDR64 => (K::Absolute, g, 64), _ => unknown, }, elf::EM_RISCV => match r_type { elf::R_RISCV_32 => (K::Absolute, g, 32), elf::R_RISCV_64 => (K::Absolute, g, 64), _ => unknown, }, elf::EM_S390 => match r_type { elf::R_390_8 => (K::Absolute, g, 8), elf::R_390_16 => (K::Absolute, g, 16), elf::R_390_32 => (K::Absolute, g, 32), elf::R_390_64 => (K::Absolute, g, 64), elf::R_390_PC16 => (K::Relative, g, 16), elf::R_390_PC32 => (K::Relative, g, 32), elf::R_390_PC64 => (K::Relative, g, 64), elf::R_390_PC16DBL => (K::Relative, E::S390xDbl, 16), elf::R_390_PC32DBL => (K::Relative, E::S390xDbl, 32), elf::R_390_PLT16DBL => (K::PltRelative, E::S390xDbl, 16), elf::R_390_PLT32DBL => (K::PltRelative, E::S390xDbl, 32), elf::R_390_GOT16 => (K::Got, g, 16), elf::R_390_GOT32 => (K::Got, g, 32), elf::R_390_GOT64 => (K::Got, g, 64), elf::R_390_GOTENT => (K::GotRelative, E::S390xDbl, 32), elf::R_390_GOTOFF16 => (K::GotBaseOffset, g, 16), elf::R_390_GOTOFF32 => (K::GotBaseOffset, g, 32), elf::R_390_GOTOFF64 => (K::GotBaseOffset, g, 64), elf::R_390_GOTPC => (K::GotBaseRelative, g, 64), elf::R_390_GOTPCDBL => (K::GotBaseRelative, E::S390xDbl, 32), _ => unknown, }, elf::EM_SBF => match r_type { elf::R_SBF_64_64 => (K::Absolute, g, 64), elf::R_SBF_64_32 => (K::Absolute, g, 32), _ => unknown, }, elf::EM_SHARC => match r_type { elf::R_SHARC_ADDR24_V3 => (K::Absolute, E::SharcTypeA, 24), elf::R_SHARC_ADDR32_V3 => (K::Absolute, E::SharcTypeA, 32), elf::R_SHARC_ADDR_VAR_V3 => (K::Absolute, E::Generic, 32), elf::R_SHARC_PCRSHORT_V3 => (K::Relative, E::SharcTypeA, 6), elf::R_SHARC_PCRLONG_V3 => (K::Relative, E::SharcTypeA, 24), elf::R_SHARC_DATA6_V3 => (K::Absolute, E::SharcTypeA, 6), elf::R_SHARC_DATA16_V3 => (K::Absolute, E::SharcTypeA, 16), elf::R_SHARC_DATA6_VISA_V3 => (K::Absolute, E::SharcTypeB, 6), elf::R_SHARC_DATA7_VISA_V3 => (K::Absolute, E::SharcTypeB, 7), elf::R_SHARC_DATA16_VISA_V3 => (K::Absolute, E::SharcTypeB, 16), elf::R_SHARC_PCR6_VISA_V3 => (K::Relative, E::SharcTypeB, 16), elf::R_SHARC_ADDR_VAR16_V3 => (K::Absolute, E::Generic, 16), _ => unknown, }, elf::EM_SPARC | elf::EM_SPARC32PLUS | elf::EM_SPARCV9 => match r_type { elf::R_SPARC_32 | elf::R_SPARC_UA32 => (K::Absolute, g, 32), elf::R_SPARC_64 | elf::R_SPARC_UA64 => (K::Absolute, g, 64), _ => unknown, }, elf::EM_XTENSA => match r_type { elf::R_XTENSA_32 => (K::Absolute, g, 32), elf::R_XTENSA_32_PCREL => (K::Relative, g, 32), _ => unknown, }, _ => unknown, }; let target = match reloc.symbol(endian, is_mips64el) { None => RelocationTarget::Absolute, Some(symbol) => RelocationTarget::Symbol(symbol), }; Relocation { kind, encoding, size, target, addend: reloc.r_addend(endian).into(), implicit_addend, flags, } } /// A trait for generic access to [`elf::Rel32`] and [`elf::Rel64`]. #[allow(missing_docs)] pub trait Rel: Debug + Pod + Clone { type Word: Into; type Sword: Into; type Endian: endian::Endian; fn r_offset(&self, endian: Self::Endian) -> Self::Word; fn r_info(&self, endian: Self::Endian) -> Self::Word; fn r_sym(&self, endian: Self::Endian) -> u32; fn r_type(&self, endian: Self::Endian) -> u32; /// Get the symbol index referenced by the relocation. /// /// Returns `None` for the null symbol index. fn symbol(&self, endian: Self::Endian) -> Option { let sym = self.r_sym(endian); if sym == 0 { None } else { Some(SymbolIndex(sym as usize)) } } } impl Rel for elf::Rel32 { type Word = u32; type Sword = i32; type Endian = Endian; #[inline] fn r_offset(&self, endian: Self::Endian) -> Self::Word { self.r_offset.get(endian) } #[inline] fn r_info(&self, endian: Self::Endian) -> Self::Word { self.r_info.get(endian) } #[inline] fn r_sym(&self, endian: Self::Endian) -> u32 { self.r_sym(endian) } #[inline] fn r_type(&self, endian: Self::Endian) -> u32 { self.r_type(endian) } } impl Rel for elf::Rel64 { type Word = u64; type Sword = i64; type Endian = Endian; #[inline] fn r_offset(&self, endian: Self::Endian) -> Self::Word { self.r_offset.get(endian) } #[inline] fn r_info(&self, endian: Self::Endian) -> Self::Word { self.r_info.get(endian) } #[inline] fn r_sym(&self, endian: Self::Endian) -> u32 { self.r_sym(endian) } #[inline] fn r_type(&self, endian: Self::Endian) -> u32 { self.r_type(endian) } } /// A trait for generic access to [`elf::Rela32`] and [`elf::Rela64`]. #[allow(missing_docs)] pub trait Rela: Debug + Pod + Clone { type Word: Into; type Sword: Into; type Endian: endian::Endian; fn r_offset(&self, endian: Self::Endian) -> Self::Word; fn r_info(&self, endian: Self::Endian, is_mips64el: bool) -> Self::Word; fn r_addend(&self, endian: Self::Endian) -> Self::Sword; fn r_sym(&self, endian: Self::Endian, is_mips64el: bool) -> u32; fn r_type(&self, endian: Self::Endian, is_mips64el: bool) -> u32; /// Get the symbol index referenced by the relocation. /// /// Returns `None` for the null symbol index. fn symbol(&self, endian: Self::Endian, is_mips64el: bool) -> Option { let sym = self.r_sym(endian, is_mips64el); if sym == 0 { None } else { Some(SymbolIndex(sym as usize)) } } } impl Rela for elf::Rela32 { type Word = u32; type Sword = i32; type Endian = Endian; #[inline] fn r_offset(&self, endian: Self::Endian) -> Self::Word { self.r_offset.get(endian) } #[inline] fn r_info(&self, endian: Self::Endian, _is_mips64el: bool) -> Self::Word { self.r_info.get(endian) } #[inline] fn r_addend(&self, endian: Self::Endian) -> Self::Sword { self.r_addend.get(endian) } #[inline] fn r_sym(&self, endian: Self::Endian, _is_mips64el: bool) -> u32 { self.r_sym(endian) } #[inline] fn r_type(&self, endian: Self::Endian, _is_mips64el: bool) -> u32 { self.r_type(endian) } } impl Rela for elf::Rela64 { type Word = u64; type Sword = i64; type Endian = Endian; #[inline] fn r_offset(&self, endian: Self::Endian) -> Self::Word { self.r_offset.get(endian) } #[inline] fn r_info(&self, endian: Self::Endian, is_mips64el: bool) -> Self::Word { self.get_r_info(endian, is_mips64el) } #[inline] fn r_addend(&self, endian: Self::Endian) -> Self::Sword { self.r_addend.get(endian) } #[inline] fn r_sym(&self, endian: Self::Endian, is_mips64el: bool) -> u32 { self.r_sym(endian, is_mips64el) } #[inline] fn r_type(&self, endian: Self::Endian, is_mips64el: bool) -> u32 { self.r_type(endian, is_mips64el) } } object-0.36.5/src/read/elf/section.rs000064400000000000000000001177101046102023000154560ustar 00000000000000use core::fmt::Debug; use core::{iter, slice, str}; use crate::elf; use crate::endian::{self, Endianness, U32Bytes}; use crate::pod::{self, Pod}; use crate::read::{ self, gnu_compression, CompressedData, CompressedFileRange, CompressionFormat, Error, ObjectSection, ReadError, ReadRef, RelocationMap, SectionFlags, SectionIndex, SectionKind, StringTable, }; use super::{ AttributesSection, CompressionHeader, ElfFile, ElfSectionRelocationIterator, FileHeader, GnuHashTable, HashTable, NoteIterator, RelocationSections, SymbolTable, VerdefIterator, VerneedIterator, VersionTable, }; /// The table of section headers in an ELF file. /// /// Also includes the string table used for the section names. /// /// Returned by [`FileHeader::sections`]. #[derive(Debug, Clone, Copy)] pub struct SectionTable<'data, Elf: FileHeader, R = &'data [u8]> where R: ReadRef<'data>, { sections: &'data [Elf::SectionHeader], strings: StringTable<'data, R>, } impl<'data, Elf: FileHeader, R: ReadRef<'data>> Default for SectionTable<'data, Elf, R> { fn default() -> Self { SectionTable { sections: &[], strings: StringTable::default(), } } } impl<'data, Elf: FileHeader, R: ReadRef<'data>> SectionTable<'data, Elf, R> { /// Create a new section table. #[inline] pub fn new(sections: &'data [Elf::SectionHeader], strings: StringTable<'data, R>) -> Self { SectionTable { sections, strings } } /// Iterate over the section headers. /// /// This includes the null section at index 0, which you will usually need to skip. #[inline] pub fn iter(&self) -> slice::Iter<'data, Elf::SectionHeader> { self.sections.iter() } /// Iterate over the section headers and their indices. /// /// This includes the null section at index 0, which you will usually need to skip. #[inline] pub fn enumerate(&self) -> impl Iterator { self.sections .iter() .enumerate() .map(|(i, section)| (SectionIndex(i), section)) } /// Return true if the section table is empty. #[inline] pub fn is_empty(&self) -> bool { self.sections.is_empty() } /// The number of section headers. #[inline] pub fn len(&self) -> usize { self.sections.len() } /// Get the section header at the given index. /// /// Returns an error for the null section at index 0. pub fn section(&self, index: SectionIndex) -> read::Result<&'data Elf::SectionHeader> { if index == SectionIndex(0) { return Err(read::Error("Invalid ELF section index")); } self.sections .get(index.0) .read_error("Invalid ELF section index") } /// Return the section header with the given name. /// /// Ignores sections with invalid names. pub fn section_by_name( &self, endian: Elf::Endian, name: &[u8], ) -> Option<(SectionIndex, &'data Elf::SectionHeader)> { self.enumerate() .find(|(_, section)| self.section_name(endian, section) == Ok(name)) } /// Return the section name for the given section header. pub fn section_name( &self, endian: Elf::Endian, section: &Elf::SectionHeader, ) -> read::Result<&'data [u8]> { section.name(endian, self.strings) } /// Return the string table at the given section index. /// /// Returns an empty string table if the index is 0. /// Returns an error if the section is not a string table. #[inline] pub fn strings( &self, endian: Elf::Endian, data: R, index: SectionIndex, ) -> read::Result> { if index == SectionIndex(0) { return Ok(StringTable::default()); } self.section(index)? .strings(endian, data)? .read_error("Invalid ELF string section type") } /// Return the symbol table of the given section type. /// /// Returns an empty symbol table if the symbol table does not exist. #[inline] pub fn symbols( &self, endian: Elf::Endian, data: R, sh_type: u32, ) -> read::Result> { debug_assert!(sh_type == elf::SHT_DYNSYM || sh_type == elf::SHT_SYMTAB); let (index, section) = match self.enumerate().find(|s| s.1.sh_type(endian) == sh_type) { Some(s) => s, None => return Ok(SymbolTable::default()), }; SymbolTable::parse(endian, data, self, index, section) } /// Return the symbol table at the given section index. /// /// Returns an error if the section is not a symbol table. #[inline] pub fn symbol_table_by_index( &self, endian: Elf::Endian, data: R, index: SectionIndex, ) -> read::Result> { let section = self.section(index)?; match section.sh_type(endian) { elf::SHT_DYNSYM | elf::SHT_SYMTAB => {} _ => return Err(Error("Invalid ELF symbol table section type")), } SymbolTable::parse(endian, data, self, index, section) } /// Create a mapping from section index to associated relocation sections. #[inline] pub fn relocation_sections( &self, endian: Elf::Endian, symbol_section: SectionIndex, ) -> read::Result { RelocationSections::parse(endian, self, symbol_section) } /// Return the contents of a dynamic section. /// /// Also returns the linked string table index. /// /// Returns `Ok(None)` if there is no `SHT_DYNAMIC` section. /// Returns `Err` for invalid values. pub fn dynamic( &self, endian: Elf::Endian, data: R, ) -> read::Result> { for section in self.sections { if let Some(dynamic) = section.dynamic(endian, data)? { return Ok(Some(dynamic)); } } Ok(None) } /// Return the header of a SysV hash section. /// /// Returns `Ok(None)` if there is no SysV GNU hash section. /// Returns `Err` for invalid values. pub fn hash_header( &self, endian: Elf::Endian, data: R, ) -> read::Result>> { for section in self.sections { if let Some(hash) = section.hash_header(endian, data)? { return Ok(Some(hash)); } } Ok(None) } /// Return the contents of a SysV hash section. /// /// Also returns the linked symbol table index. /// /// Returns `Ok(None)` if there is no SysV hash section. /// Returns `Err` for invalid values. pub fn hash( &self, endian: Elf::Endian, data: R, ) -> read::Result, SectionIndex)>> { for section in self.sections { if let Some(hash) = section.hash(endian, data)? { return Ok(Some(hash)); } } Ok(None) } /// Return the header of a GNU hash section. /// /// Returns `Ok(None)` if there is no GNU hash section. /// Returns `Err` for invalid values. pub fn gnu_hash_header( &self, endian: Elf::Endian, data: R, ) -> read::Result>> { for section in self.sections { if let Some(hash) = section.gnu_hash_header(endian, data)? { return Ok(Some(hash)); } } Ok(None) } /// Return the contents of a GNU hash section. /// /// Also returns the linked symbol table index. /// /// Returns `Ok(None)` if there is no GNU hash section. /// Returns `Err` for invalid values. pub fn gnu_hash( &self, endian: Elf::Endian, data: R, ) -> read::Result, SectionIndex)>> { for section in self.sections { if let Some(hash) = section.gnu_hash(endian, data)? { return Ok(Some(hash)); } } Ok(None) } /// Return the contents of a `SHT_GNU_VERSYM` section. /// /// Also returns the linked symbol table index. /// /// Returns `Ok(None)` if there is no `SHT_GNU_VERSYM` section. /// Returns `Err` for invalid values. pub fn gnu_versym( &self, endian: Elf::Endian, data: R, ) -> read::Result], SectionIndex)>> { for section in self.sections { if let Some(syms) = section.gnu_versym(endian, data)? { return Ok(Some(syms)); } } Ok(None) } /// Return the contents of a `SHT_GNU_VERDEF` section. /// /// Also returns the linked string table index. /// /// Returns `Ok(None)` if there is no `SHT_GNU_VERDEF` section. /// Returns `Err` for invalid values. pub fn gnu_verdef( &self, endian: Elf::Endian, data: R, ) -> read::Result, SectionIndex)>> { for section in self.sections { if let Some(defs) = section.gnu_verdef(endian, data)? { return Ok(Some(defs)); } } Ok(None) } /// Return the contents of a `SHT_GNU_VERNEED` section. /// /// Also returns the linked string table index. /// /// Returns `Ok(None)` if there is no `SHT_GNU_VERNEED` section. /// Returns `Err` for invalid values. pub fn gnu_verneed( &self, endian: Elf::Endian, data: R, ) -> read::Result, SectionIndex)>> { for section in self.sections { if let Some(needs) = section.gnu_verneed(endian, data)? { return Ok(Some(needs)); } } Ok(None) } /// Returns the symbol version table. /// /// Returns `Ok(None)` if there is no `SHT_GNU_VERSYM` section. /// Returns `Err` for invalid values. pub fn versions( &self, endian: Elf::Endian, data: R, ) -> read::Result>> { let (versyms, link) = match self.gnu_versym(endian, data)? { Some(val) => val, None => return Ok(None), }; let strings = self.symbol_table_by_index(endian, data, link)?.strings(); // TODO: check links? let verdefs = self.gnu_verdef(endian, data)?.map(|x| x.0); let verneeds = self.gnu_verneed(endian, data)?.map(|x| x.0); VersionTable::parse(endian, versyms, verdefs, verneeds, strings).map(Some) } } /// An iterator for the sections in an [`ElfFile32`](super::ElfFile32). pub type ElfSectionIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfSectionIterator<'data, 'file, elf::FileHeader32, R>; /// An iterator for the sections in an [`ElfFile64`](super::ElfFile64). pub type ElfSectionIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfSectionIterator<'data, 'file, elf::FileHeader64, R>; /// An iterator for the sections in an [`ElfFile`]. #[derive(Debug)] pub struct ElfSectionIterator<'data, 'file, Elf, R = &'data [u8]> where Elf: FileHeader, R: ReadRef<'data>, { file: &'file ElfFile<'data, Elf, R>, iter: iter::Enumerate>, } impl<'data, 'file, Elf, R> ElfSectionIterator<'data, 'file, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { pub(super) fn new(file: &'file ElfFile<'data, Elf, R>) -> Self { let mut iter = file.sections.iter().enumerate(); iter.next(); // Skip null section. ElfSectionIterator { file, iter } } } impl<'data, 'file, Elf, R> Iterator for ElfSectionIterator<'data, 'file, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { type Item = ElfSection<'data, 'file, Elf, R>; fn next(&mut self) -> Option { self.iter.next().map(|(index, section)| ElfSection { index: SectionIndex(index), file: self.file, section, }) } } /// A section in an [`ElfFile32`](super::ElfFile32). pub type ElfSection32<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfSection<'data, 'file, elf::FileHeader32, R>; /// A section in an [`ElfFile64`](super::ElfFile64). pub type ElfSection64<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfSection<'data, 'file, elf::FileHeader64, R>; /// A section in an [`ElfFile`]. /// /// Most functionality is provided by the [`ObjectSection`] trait implementation. #[derive(Debug)] pub struct ElfSection<'data, 'file, Elf, R = &'data [u8]> where Elf: FileHeader, R: ReadRef<'data>, { pub(super) file: &'file ElfFile<'data, Elf, R>, pub(super) index: SectionIndex, pub(super) section: &'data Elf::SectionHeader, } impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> ElfSection<'data, 'file, Elf, R> { /// Get the ELF file containing this section. pub fn elf_file(&self) -> &'file ElfFile<'data, Elf, R> { self.file } /// Get the raw ELF section header. pub fn elf_section_header(&self) -> &'data Elf::SectionHeader { self.section } /// Get the index of the relocation section that references this section. /// /// Returns `None` if there are no relocations. /// Returns an error if there are multiple relocation sections that reference this section. pub fn elf_relocation_section_index(&self) -> read::Result> { let Some(relocation_index) = self.file.relocations.get(self.index) else { return Ok(None); }; if self.file.relocations.get(relocation_index).is_some() { return Err(Error( "Unsupported ELF section with multiple relocation sections", )); } Ok(Some(relocation_index)) } /// Get the relocation section that references this section. /// /// Returns `None` if there are no relocations. /// Returns an error if there are multiple relocation sections that reference this section. pub fn elf_relocation_section(&self) -> read::Result> { let Some(relocation_index) = self.elf_relocation_section_index()? else { return Ok(None); }; self.file.sections.section(relocation_index).map(Some) } /// Get the `Elf::Rel` entries that apply to this section. /// /// Returns an empty slice if there are no relocations. /// Returns an error if there are multiple relocation sections that reference this section. pub fn elf_linked_rel(&self) -> read::Result<&'data [Elf::Rel]> { let Some(relocation_section) = self.elf_relocation_section()? else { return Ok(&[]); }; // The linked symbol table was already checked when self.file.relocations was created. let Some((rel, _)) = relocation_section.rel(self.file.endian, self.file.data)? else { return Ok(&[]); }; Ok(rel) } /// Get the `Elf::Rela` entries that apply to this section. /// /// Returns an empty slice if there are no relocations. /// Returns an error if there are multiple relocation sections that reference this section. pub fn elf_linked_rela(&self) -> read::Result<&'data [Elf::Rela]> { let Some(relocation_section) = self.elf_relocation_section()? else { return Ok(&[]); }; // The linked symbol table was already checked when self.file.relocations was created. let Some((rela, _)) = relocation_section.rela(self.file.endian, self.file.data)? else { return Ok(&[]); }; Ok(rela) } fn bytes(&self) -> read::Result<&'data [u8]> { self.section .data(self.file.endian, self.file.data) .read_error("Invalid ELF section size or offset") } fn maybe_compressed(&self) -> read::Result> { let endian = self.file.endian; if let Some((header, offset, compressed_size)) = self.section.compression(endian, self.file.data)? { let format = match header.ch_type(endian) { elf::ELFCOMPRESS_ZLIB => CompressionFormat::Zlib, elf::ELFCOMPRESS_ZSTD => CompressionFormat::Zstandard, _ => return Err(Error("Unsupported ELF compression type")), }; let uncompressed_size = header.ch_size(endian).into(); Ok(Some(CompressedFileRange { format, offset, compressed_size, uncompressed_size, })) } else { Ok(None) } } // Try GNU-style "ZLIB" header decompression. fn maybe_compressed_gnu(&self) -> read::Result> { if !self .name() .map_or(false, |name| name.starts_with(".zdebug_")) { return Ok(None); } let (section_offset, section_size) = self .file_range() .read_error("Invalid ELF GNU compressed section type")?; gnu_compression::compressed_file_range(self.file.data, section_offset, section_size) .map(Some) } } impl<'data, 'file, Elf, R> read::private::Sealed for ElfSection<'data, 'file, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { } impl<'data, 'file, Elf, R> ObjectSection<'data> for ElfSection<'data, 'file, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { type RelocationIterator = ElfSectionRelocationIterator<'data, 'file, Elf, R>; #[inline] fn index(&self) -> SectionIndex { self.index } #[inline] fn address(&self) -> u64 { self.section.sh_addr(self.file.endian).into() } #[inline] fn size(&self) -> u64 { self.section.sh_size(self.file.endian).into() } #[inline] fn align(&self) -> u64 { self.section.sh_addralign(self.file.endian).into() } #[inline] fn file_range(&self) -> Option<(u64, u64)> { self.section.file_range(self.file.endian) } #[inline] fn data(&self) -> read::Result<&'data [u8]> { self.bytes() } fn data_range(&self, address: u64, size: u64) -> read::Result> { Ok(read::util::data_range( self.bytes()?, self.address(), address, size, )) } fn compressed_file_range(&self) -> read::Result { Ok(if let Some(data) = self.maybe_compressed()? { data } else if let Some(data) = self.maybe_compressed_gnu()? { data } else { CompressedFileRange::none(self.file_range()) }) } fn compressed_data(&self) -> read::Result> { self.compressed_file_range()?.data(self.file.data) } fn name_bytes(&self) -> read::Result<&'data [u8]> { self.file .sections .section_name(self.file.endian, self.section) } fn name(&self) -> read::Result<&'data str> { let name = self.name_bytes()?; str::from_utf8(name) .ok() .read_error("Non UTF-8 ELF section name") } #[inline] fn segment_name_bytes(&self) -> read::Result> { Ok(None) } #[inline] fn segment_name(&self) -> read::Result> { Ok(None) } fn kind(&self) -> SectionKind { let flags = self.section.sh_flags(self.file.endian).into(); let sh_type = self.section.sh_type(self.file.endian); match sh_type { elf::SHT_PROGBITS => { if flags & u64::from(elf::SHF_ALLOC) != 0 { if flags & u64::from(elf::SHF_EXECINSTR) != 0 { SectionKind::Text } else if flags & u64::from(elf::SHF_TLS) != 0 { SectionKind::Tls } else if flags & u64::from(elf::SHF_WRITE) != 0 { SectionKind::Data } else if flags & u64::from(elf::SHF_STRINGS) != 0 { SectionKind::ReadOnlyString } else { SectionKind::ReadOnlyData } } else if flags & u64::from(elf::SHF_STRINGS) != 0 { SectionKind::OtherString } else { SectionKind::Other } } elf::SHT_NOBITS => { if flags & u64::from(elf::SHF_TLS) != 0 { SectionKind::UninitializedTls } else { SectionKind::UninitializedData } } elf::SHT_NOTE => SectionKind::Note, elf::SHT_NULL | elf::SHT_SYMTAB | elf::SHT_STRTAB | elf::SHT_RELA | elf::SHT_HASH | elf::SHT_DYNAMIC | elf::SHT_REL | elf::SHT_DYNSYM | elf::SHT_GROUP => SectionKind::Metadata, _ => SectionKind::Elf(sh_type), } } fn relocations(&self) -> ElfSectionRelocationIterator<'data, 'file, Elf, R> { ElfSectionRelocationIterator { section_index: self.index, file: self.file, relocations: None, } } fn relocation_map(&self) -> read::Result { RelocationMap::new(self.file, self) } fn flags(&self) -> SectionFlags { SectionFlags::Elf { sh_flags: self.section.sh_flags(self.file.endian).into(), } } } /// A trait for generic access to [`elf::SectionHeader32`] and [`elf::SectionHeader64`]. #[allow(missing_docs)] pub trait SectionHeader: Debug + Pod { type Elf: FileHeader; type Word: Into; type Endian: endian::Endian; fn sh_name(&self, endian: Self::Endian) -> u32; fn sh_type(&self, endian: Self::Endian) -> u32; fn sh_flags(&self, endian: Self::Endian) -> Self::Word; fn sh_addr(&self, endian: Self::Endian) -> Self::Word; fn sh_offset(&self, endian: Self::Endian) -> Self::Word; fn sh_size(&self, endian: Self::Endian) -> Self::Word; fn sh_link(&self, endian: Self::Endian) -> u32; fn sh_info(&self, endian: Self::Endian) -> u32; fn sh_addralign(&self, endian: Self::Endian) -> Self::Word; fn sh_entsize(&self, endian: Self::Endian) -> Self::Word; /// Parse the section name from the string table. fn name<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, strings: StringTable<'data, R>, ) -> read::Result<&'data [u8]> { strings .get(self.sh_name(endian)) .read_error("Invalid ELF section name offset") } /// Get the `sh_link` field as a section index. /// /// This may return a null section index, and does not check for validity. fn link(&self, endian: Self::Endian) -> SectionIndex { SectionIndex(self.sh_link(endian) as usize) } /// Return true if the `SHF_INFO_LINK` flag is set. fn has_info_link(&self, endian: Self::Endian) -> bool { self.sh_flags(endian).into() & u64::from(elf::SHF_INFO_LINK) != 0 } /// Get the `sh_info` field as a section index. /// /// This does not check the `SHF_INFO_LINK` flag. /// This may return a null section index, and does not check for validity. fn info_link(&self, endian: Self::Endian) -> SectionIndex { SectionIndex(self.sh_info(endian) as usize) } /// Return the offset and size of the section in the file. /// /// Returns `None` for sections that have no data in the file. fn file_range(&self, endian: Self::Endian) -> Option<(u64, u64)> { if self.sh_type(endian) == elf::SHT_NOBITS { None } else { Some((self.sh_offset(endian).into(), self.sh_size(endian).into())) } } /// Return the section data. /// /// Returns `Ok(&[])` if the section has no data. /// Returns `Err` for invalid values. fn data<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result<&'data [u8]> { if let Some((offset, size)) = self.file_range(endian) { data.read_bytes_at(offset, size) .read_error("Invalid ELF section size or offset") } else { Ok(&[]) } } /// Return the section data as a slice of the given type. /// /// Allows padding at the end of the data. /// Returns `Ok(&[])` if the section has no data. /// Returns `Err` for invalid values, including bad alignment. fn data_as_array<'data, T: Pod, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result<&'data [T]> { pod::slice_from_all_bytes(self.data(endian, data)?) .read_error("Invalid ELF section size or offset") } /// Return the strings in the section. /// /// Returns `Ok(None)` if the section does not contain strings. /// Returns `Err` for invalid values. fn strings<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result>> { if self.sh_type(endian) != elf::SHT_STRTAB { return Ok(None); } let str_offset = self.sh_offset(endian).into(); let str_size = self.sh_size(endian).into(); let str_end = str_offset .checked_add(str_size) .read_error("Invalid ELF string section offset or size")?; Ok(Some(StringTable::new(data, str_offset, str_end))) } /// Return the symbols in the section. /// /// Also finds the linked string table in `sections`. /// /// `section_index` must be the 0-based index of this section, and is used /// to find the corresponding extended section index table in `sections`. /// /// Returns `Ok(None)` if the section does not contain symbols. /// Returns `Err` for invalid values. fn symbols<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, sections: &SectionTable<'data, Self::Elf, R>, section_index: SectionIndex, ) -> read::Result>> { let sh_type = self.sh_type(endian); if sh_type != elf::SHT_SYMTAB && sh_type != elf::SHT_DYNSYM { return Ok(None); } SymbolTable::parse(endian, data, sections, section_index, self).map(Some) } /// Return the `Elf::Rel` entries in the section. /// /// Also returns the linked symbol table index. /// /// Returns `Ok(None)` if the section does not contain relocations. /// Returns `Err` for invalid values. fn rel<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result::Rel], SectionIndex)>> { if self.sh_type(endian) != elf::SHT_REL { return Ok(None); } let rel = self .data_as_array(endian, data) .read_error("Invalid ELF relocation section offset or size")?; Ok(Some((rel, self.link(endian)))) } /// Return the `Elf::Rela` entries in the section. /// /// Also returns the linked symbol table index. /// /// Returns `Ok(None)` if the section does not contain relocations. /// Returns `Err` for invalid values. fn rela<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result::Rela], SectionIndex)>> { if self.sh_type(endian) != elf::SHT_RELA { return Ok(None); } let rela = self .data_as_array(endian, data) .read_error("Invalid ELF relocation section offset or size")?; Ok(Some((rela, self.link(endian)))) } /// Return entries in a dynamic section. /// /// Also returns the linked string table index. /// /// Returns `Ok(None)` if the section type is not `SHT_DYNAMIC`. /// Returns `Err` for invalid values. fn dynamic<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result::Dyn], SectionIndex)>> { if self.sh_type(endian) != elf::SHT_DYNAMIC { return Ok(None); } let dynamic = self .data_as_array(endian, data) .read_error("Invalid ELF dynamic section offset or size")?; Ok(Some((dynamic, self.link(endian)))) } /// Return a note iterator for the section data. /// /// Returns `Ok(None)` if the section does not contain notes. /// Returns `Err` for invalid values. fn notes<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result>> { if self.sh_type(endian) != elf::SHT_NOTE { return Ok(None); } let data = self .data(endian, data) .read_error("Invalid ELF note section offset or size")?; let notes = NoteIterator::new(endian, self.sh_addralign(endian), data)?; Ok(Some(notes)) } /// Return the contents of a group section. /// /// The first value is a `GRP_*` value, and the remaining values /// are section indices. /// /// Returns `Ok(None)` if the section does not define a group. /// Returns `Err` for invalid values. fn group<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result])>> { if self.sh_type(endian) != elf::SHT_GROUP { return Ok(None); } let msg = "Invalid ELF group section offset or size"; let data = self.data(endian, data).read_error(msg)?; let (flag, data) = pod::from_bytes::>(data).read_error(msg)?; let sections = pod::slice_from_all_bytes(data).read_error(msg)?; Ok(Some((flag.get(endian), sections))) } /// Return the header of a SysV hash section. /// /// Returns `Ok(None)` if the section does not contain a SysV hash. /// Returns `Err` for invalid values. fn hash_header<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result>> { if self.sh_type(endian) != elf::SHT_HASH { return Ok(None); } let data = self .data(endian, data) .read_error("Invalid ELF hash section offset or size")?; let header = data .read_at::>(0) .read_error("Invalid hash header")?; Ok(Some(header)) } /// Return the contents of a SysV hash section. /// /// Also returns the linked symbol table index. /// /// Returns `Ok(None)` if the section does not contain a SysV hash. /// Returns `Err` for invalid values. fn hash<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result, SectionIndex)>> { if self.sh_type(endian) != elf::SHT_HASH { return Ok(None); } let data = self .data(endian, data) .read_error("Invalid ELF hash section offset or size")?; let hash = HashTable::parse(endian, data)?; Ok(Some((hash, self.link(endian)))) } /// Return the header of a GNU hash section. /// /// Returns `Ok(None)` if the section does not contain a GNU hash. /// Returns `Err` for invalid values. fn gnu_hash_header<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result>> { if self.sh_type(endian) != elf::SHT_GNU_HASH { return Ok(None); } let data = self .data(endian, data) .read_error("Invalid ELF GNU hash section offset or size")?; let header = data .read_at::>(0) .read_error("Invalid GNU hash header")?; Ok(Some(header)) } /// Return the contents of a GNU hash section. /// /// Also returns the linked symbol table index. /// /// Returns `Ok(None)` if the section does not contain a GNU hash. /// Returns `Err` for invalid values. fn gnu_hash<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result, SectionIndex)>> { if self.sh_type(endian) != elf::SHT_GNU_HASH { return Ok(None); } let data = self .data(endian, data) .read_error("Invalid ELF GNU hash section offset or size")?; let hash = GnuHashTable::parse(endian, data)?; Ok(Some((hash, self.link(endian)))) } /// Return the contents of a `SHT_GNU_VERSYM` section. /// /// Also returns the linked symbol table index. /// /// Returns `Ok(None)` if the section type is not `SHT_GNU_VERSYM`. /// Returns `Err` for invalid values. fn gnu_versym<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result], SectionIndex)>> { if self.sh_type(endian) != elf::SHT_GNU_VERSYM { return Ok(None); } let versym = self .data_as_array(endian, data) .read_error("Invalid ELF GNU versym section offset or size")?; Ok(Some((versym, self.link(endian)))) } /// Return an iterator for the entries of a `SHT_GNU_VERDEF` section. /// /// Also returns the linked string table index. /// /// Returns `Ok(None)` if the section type is not `SHT_GNU_VERDEF`. /// Returns `Err` for invalid values. fn gnu_verdef<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result, SectionIndex)>> { if self.sh_type(endian) != elf::SHT_GNU_VERDEF { return Ok(None); } let verdef = self .data(endian, data) .read_error("Invalid ELF GNU verdef section offset or size")?; Ok(Some(( VerdefIterator::new(endian, verdef), self.link(endian), ))) } /// Return an iterator for the entries of a `SHT_GNU_VERNEED` section. /// /// Also returns the linked string table index. /// /// Returns `Ok(None)` if the section type is not `SHT_GNU_VERNEED`. /// Returns `Err` for invalid values. fn gnu_verneed<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result, SectionIndex)>> { if self.sh_type(endian) != elf::SHT_GNU_VERNEED { return Ok(None); } let verneed = self .data(endian, data) .read_error("Invalid ELF GNU verneed section offset or size")?; Ok(Some(( VerneedIterator::new(endian, verneed), self.link(endian), ))) } /// Return the contents of a `SHT_GNU_ATTRIBUTES` section. /// /// Returns `Ok(None)` if the section type is not `SHT_GNU_ATTRIBUTES`. /// Returns `Err` for invalid values. fn gnu_attributes<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result>> { if self.sh_type(endian) != elf::SHT_GNU_ATTRIBUTES { return Ok(None); } self.attributes(endian, data).map(Some) } /// Parse the contents of the section as attributes. /// /// This function does not check whether section type corresponds /// to a section that contains attributes. /// /// Returns `Err` for invalid values. fn attributes<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result> { let data = self.data(endian, data)?; AttributesSection::new(endian, data) } /// Parse the compression header if present. /// /// Returns the header, and the offset and size of the compressed section data /// in the file. /// /// Returns `Ok(None)` if the section flags do not have `SHF_COMPRESSED`. /// Returns `Err` for invalid values. fn compression<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result< Option<( &'data ::CompressionHeader, u64, u64, )>, > { if (self.sh_flags(endian).into() & u64::from(elf::SHF_COMPRESSED)) == 0 { return Ok(None); } let (section_offset, section_size) = self .file_range(endian) .read_error("Invalid ELF compressed section type")?; let mut offset = section_offset; let header = data .read::<::CompressionHeader>(&mut offset) .read_error("Invalid ELF compressed section offset")?; let compressed_size = section_size .checked_sub(offset - section_offset) .read_error("Invalid ELF compressed section size")?; Ok(Some((header, offset, compressed_size))) } } impl SectionHeader for elf::SectionHeader32 { type Elf = elf::FileHeader32; type Word = u32; type Endian = Endian; #[inline] fn sh_name(&self, endian: Self::Endian) -> u32 { self.sh_name.get(endian) } #[inline] fn sh_type(&self, endian: Self::Endian) -> u32 { self.sh_type.get(endian) } #[inline] fn sh_flags(&self, endian: Self::Endian) -> Self::Word { self.sh_flags.get(endian) } #[inline] fn sh_addr(&self, endian: Self::Endian) -> Self::Word { self.sh_addr.get(endian) } #[inline] fn sh_offset(&self, endian: Self::Endian) -> Self::Word { self.sh_offset.get(endian) } #[inline] fn sh_size(&self, endian: Self::Endian) -> Self::Word { self.sh_size.get(endian) } #[inline] fn sh_link(&self, endian: Self::Endian) -> u32 { self.sh_link.get(endian) } #[inline] fn sh_info(&self, endian: Self::Endian) -> u32 { self.sh_info.get(endian) } #[inline] fn sh_addralign(&self, endian: Self::Endian) -> Self::Word { self.sh_addralign.get(endian) } #[inline] fn sh_entsize(&self, endian: Self::Endian) -> Self::Word { self.sh_entsize.get(endian) } } impl SectionHeader for elf::SectionHeader64 { type Word = u64; type Endian = Endian; type Elf = elf::FileHeader64; #[inline] fn sh_name(&self, endian: Self::Endian) -> u32 { self.sh_name.get(endian) } #[inline] fn sh_type(&self, endian: Self::Endian) -> u32 { self.sh_type.get(endian) } #[inline] fn sh_flags(&self, endian: Self::Endian) -> Self::Word { self.sh_flags.get(endian) } #[inline] fn sh_addr(&self, endian: Self::Endian) -> Self::Word { self.sh_addr.get(endian) } #[inline] fn sh_offset(&self, endian: Self::Endian) -> Self::Word { self.sh_offset.get(endian) } #[inline] fn sh_size(&self, endian: Self::Endian) -> Self::Word { self.sh_size.get(endian) } #[inline] fn sh_link(&self, endian: Self::Endian) -> u32 { self.sh_link.get(endian) } #[inline] fn sh_info(&self, endian: Self::Endian) -> u32 { self.sh_info.get(endian) } #[inline] fn sh_addralign(&self, endian: Self::Endian) -> Self::Word { self.sh_addralign.get(endian) } #[inline] fn sh_entsize(&self, endian: Self::Endian) -> Self::Word { self.sh_entsize.get(endian) } } object-0.36.5/src/read/elf/segment.rs000064400000000000000000000245621046102023000154560ustar 00000000000000use core::fmt::Debug; use core::{slice, str}; use crate::elf; use crate::endian::{self, Endianness}; use crate::pod::{self, Pod}; use crate::read::{self, ObjectSegment, ReadError, ReadRef, SegmentFlags}; use super::{ElfFile, FileHeader, NoteIterator}; /// An iterator for the segments in an [`ElfFile32`](super::ElfFile32). pub type ElfSegmentIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfSegmentIterator<'data, 'file, elf::FileHeader32, R>; /// An iterator for the segments in an [`ElfFile64`](super::ElfFile64). pub type ElfSegmentIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfSegmentIterator<'data, 'file, elf::FileHeader64, R>; /// An iterator for the segments in an [`ElfFile`]. #[derive(Debug)] pub struct ElfSegmentIterator<'data, 'file, Elf, R = &'data [u8]> where Elf: FileHeader, R: ReadRef<'data>, { pub(super) file: &'file ElfFile<'data, Elf, R>, pub(super) iter: slice::Iter<'data, Elf::ProgramHeader>, } impl<'data, 'file, Elf, R> Iterator for ElfSegmentIterator<'data, 'file, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { type Item = ElfSegment<'data, 'file, Elf, R>; fn next(&mut self) -> Option { for segment in self.iter.by_ref() { if segment.p_type(self.file.endian) == elf::PT_LOAD { return Some(ElfSegment { file: self.file, segment, }); } } None } } /// A segment in an [`ElfFile32`](super::ElfFile32). pub type ElfSegment32<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfSegment<'data, 'file, elf::FileHeader32, R>; /// A segment in an [`ElfFile64`](super::ElfFile64). pub type ElfSegment64<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfSegment<'data, 'file, elf::FileHeader64, R>; /// A segment in an [`ElfFile`]. /// /// Most functionality is provided by the [`ObjectSegment`] trait implementation. #[derive(Debug)] pub struct ElfSegment<'data, 'file, Elf, R = &'data [u8]> where Elf: FileHeader, R: ReadRef<'data>, { pub(super) file: &'file ElfFile<'data, Elf, R>, pub(super) segment: &'data Elf::ProgramHeader, } impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> ElfSegment<'data, 'file, Elf, R> { /// Get the ELF file containing this segment. pub fn elf_file(&self) -> &'file ElfFile<'data, Elf, R> { self.file } /// Get the raw ELF program header for the segment. pub fn elf_program_header(&self) -> &'data Elf::ProgramHeader { self.segment } fn bytes(&self) -> read::Result<&'data [u8]> { self.segment .data(self.file.endian, self.file.data) .read_error("Invalid ELF segment size or offset") } } impl<'data, 'file, Elf, R> read::private::Sealed for ElfSegment<'data, 'file, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { } impl<'data, 'file, Elf, R> ObjectSegment<'data> for ElfSegment<'data, 'file, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { #[inline] fn address(&self) -> u64 { self.segment.p_vaddr(self.file.endian).into() } #[inline] fn size(&self) -> u64 { self.segment.p_memsz(self.file.endian).into() } #[inline] fn align(&self) -> u64 { self.segment.p_align(self.file.endian).into() } #[inline] fn file_range(&self) -> (u64, u64) { self.segment.file_range(self.file.endian) } #[inline] fn data(&self) -> read::Result<&'data [u8]> { self.bytes() } fn data_range(&self, address: u64, size: u64) -> read::Result> { Ok(read::util::data_range( self.bytes()?, self.address(), address, size, )) } #[inline] fn name_bytes(&self) -> read::Result> { Ok(None) } #[inline] fn name(&self) -> read::Result> { Ok(None) } #[inline] fn flags(&self) -> SegmentFlags { let p_flags = self.segment.p_flags(self.file.endian); SegmentFlags::Elf { p_flags } } } /// A trait for generic access to [`elf::ProgramHeader32`] and [`elf::ProgramHeader64`]. #[allow(missing_docs)] pub trait ProgramHeader: Debug + Pod { type Elf: FileHeader; type Word: Into; type Endian: endian::Endian; fn p_type(&self, endian: Self::Endian) -> u32; fn p_flags(&self, endian: Self::Endian) -> u32; fn p_offset(&self, endian: Self::Endian) -> Self::Word; fn p_vaddr(&self, endian: Self::Endian) -> Self::Word; fn p_paddr(&self, endian: Self::Endian) -> Self::Word; fn p_filesz(&self, endian: Self::Endian) -> Self::Word; fn p_memsz(&self, endian: Self::Endian) -> Self::Word; fn p_align(&self, endian: Self::Endian) -> Self::Word; /// Return the offset and size of the segment in the file. fn file_range(&self, endian: Self::Endian) -> (u64, u64) { (self.p_offset(endian).into(), self.p_filesz(endian).into()) } /// Return the segment data. /// /// Returns `Err` for invalid values. fn data<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> Result<&'data [u8], ()> { let (offset, size) = self.file_range(endian); data.read_bytes_at(offset, size) } /// Return the segment data as a slice of the given type. /// /// Allows padding at the end of the data. /// Returns `Ok(&[])` if the segment has no data. /// Returns `Err` for invalid values, including bad alignment. fn data_as_array<'data, T: Pod, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> Result<&'data [T], ()> { pod::slice_from_all_bytes(self.data(endian, data)?) } /// Return the segment data in the given virtual address range /// /// Returns `Ok(None)` if the segment does not contain the address. /// Returns `Err` for invalid values. fn data_range<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, address: u64, size: u64, ) -> Result, ()> { Ok(read::util::data_range( self.data(endian, data)?, self.p_vaddr(endian).into(), address, size, )) } /// Return entries in a dynamic segment. /// /// Returns `Ok(None)` if the segment is not `PT_DYNAMIC`. /// Returns `Err` for invalid values. fn dynamic<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result::Dyn]>> { if self.p_type(endian) != elf::PT_DYNAMIC { return Ok(None); } let dynamic = self .data_as_array(endian, data) .read_error("Invalid ELF dynamic segment offset or size")?; Ok(Some(dynamic)) } /// Return the data in an interpreter segment. /// /// Returns `Ok(None)` if the segment is not `PT_INTERP`. /// Returns `Err` for invalid values. fn interpreter<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result> { if self.p_type(endian) != elf::PT_INTERP { return Ok(None); } let data = self .data(endian, data) .read_error("Invalid ELF interpreter segment offset or size")?; let len = data .iter() .position(|&b| b == 0) .read_error("Invalid ELF interpreter segment data")?; Ok(Some(&data[..len])) } /// Return a note iterator for the segment data. /// /// Returns `Ok(None)` if the segment does not contain notes. /// Returns `Err` for invalid values. fn notes<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result>> { if self.p_type(endian) != elf::PT_NOTE { return Ok(None); } let data = self .data(endian, data) .read_error("Invalid ELF note segment offset or size")?; let notes = NoteIterator::new(endian, self.p_align(endian), data)?; Ok(Some(notes)) } } impl ProgramHeader for elf::ProgramHeader32 { type Word = u32; type Endian = Endian; type Elf = elf::FileHeader32; #[inline] fn p_type(&self, endian: Self::Endian) -> u32 { self.p_type.get(endian) } #[inline] fn p_flags(&self, endian: Self::Endian) -> u32 { self.p_flags.get(endian) } #[inline] fn p_offset(&self, endian: Self::Endian) -> Self::Word { self.p_offset.get(endian) } #[inline] fn p_vaddr(&self, endian: Self::Endian) -> Self::Word { self.p_vaddr.get(endian) } #[inline] fn p_paddr(&self, endian: Self::Endian) -> Self::Word { self.p_paddr.get(endian) } #[inline] fn p_filesz(&self, endian: Self::Endian) -> Self::Word { self.p_filesz.get(endian) } #[inline] fn p_memsz(&self, endian: Self::Endian) -> Self::Word { self.p_memsz.get(endian) } #[inline] fn p_align(&self, endian: Self::Endian) -> Self::Word { self.p_align.get(endian) } } impl ProgramHeader for elf::ProgramHeader64 { type Word = u64; type Endian = Endian; type Elf = elf::FileHeader64; #[inline] fn p_type(&self, endian: Self::Endian) -> u32 { self.p_type.get(endian) } #[inline] fn p_flags(&self, endian: Self::Endian) -> u32 { self.p_flags.get(endian) } #[inline] fn p_offset(&self, endian: Self::Endian) -> Self::Word { self.p_offset.get(endian) } #[inline] fn p_vaddr(&self, endian: Self::Endian) -> Self::Word { self.p_vaddr.get(endian) } #[inline] fn p_paddr(&self, endian: Self::Endian) -> Self::Word { self.p_paddr.get(endian) } #[inline] fn p_filesz(&self, endian: Self::Endian) -> Self::Word { self.p_filesz.get(endian) } #[inline] fn p_memsz(&self, endian: Self::Endian) -> Self::Word { self.p_memsz.get(endian) } #[inline] fn p_align(&self, endian: Self::Endian) -> Self::Word { self.p_align.get(endian) } } object-0.36.5/src/read/elf/symbol.rs000064400000000000000000000446171046102023000153240ustar 00000000000000use alloc::fmt; use alloc::vec::Vec; use core::fmt::Debug; use core::slice; use core::str; use crate::elf; use crate::endian::{self, Endianness, U32}; use crate::pod::Pod; use crate::read::util::StringTable; use crate::read::{ self, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, SectionIndex, SymbolFlags, SymbolIndex, SymbolKind, SymbolMap, SymbolMapEntry, SymbolScope, SymbolSection, }; use super::{FileHeader, SectionHeader, SectionTable}; /// A table of symbol entries in an ELF file. /// /// Also includes the string table used for the symbol names. /// /// Returned by [`SectionTable::symbols`]. #[derive(Debug, Clone, Copy)] pub struct SymbolTable<'data, Elf: FileHeader, R = &'data [u8]> where R: ReadRef<'data>, { section: SectionIndex, string_section: SectionIndex, shndx_section: SectionIndex, symbols: &'data [Elf::Sym], strings: StringTable<'data, R>, shndx: &'data [U32], } impl<'data, Elf: FileHeader, R: ReadRef<'data>> Default for SymbolTable<'data, Elf, R> { fn default() -> Self { SymbolTable { section: SectionIndex(0), string_section: SectionIndex(0), shndx_section: SectionIndex(0), symbols: &[], strings: Default::default(), shndx: &[], } } } impl<'data, Elf: FileHeader, R: ReadRef<'data>> SymbolTable<'data, Elf, R> { /// Parse the given symbol table section. pub fn parse( endian: Elf::Endian, data: R, sections: &SectionTable<'data, Elf, R>, section_index: SectionIndex, section: &Elf::SectionHeader, ) -> read::Result> { debug_assert!( section.sh_type(endian) == elf::SHT_DYNSYM || section.sh_type(endian) == elf::SHT_SYMTAB ); let symbols = section .data_as_array(endian, data) .read_error("Invalid ELF symbol table data")?; let link = SectionIndex(section.sh_link(endian) as usize); let strings = sections.strings(endian, data, link)?; let mut shndx_section = SectionIndex(0); let mut shndx = &[][..]; for (i, s) in sections.enumerate() { if s.sh_type(endian) == elf::SHT_SYMTAB_SHNDX && s.link(endian) == section_index { shndx_section = i; shndx = s .data_as_array(endian, data) .read_error("Invalid ELF symtab_shndx data")?; } } Ok(SymbolTable { section: section_index, string_section: link, symbols, strings, shndx, shndx_section, }) } /// Return the section index of this symbol table. #[inline] pub fn section(&self) -> SectionIndex { self.section } /// Return the section index of the shndx table. #[inline] pub fn shndx_section(&self) -> SectionIndex { self.shndx_section } /// Return the section index of the linked string table. #[inline] pub fn string_section(&self) -> SectionIndex { self.string_section } /// Return the string table used for the symbol names. #[inline] pub fn strings(&self) -> StringTable<'data, R> { self.strings } /// Return the symbol table. #[inline] pub fn symbols(&self) -> &'data [Elf::Sym] { self.symbols } /// Iterate over the symbols. /// /// This includes the null symbol at index 0, which you will usually need to skip. #[inline] pub fn iter(&self) -> slice::Iter<'data, Elf::Sym> { self.symbols.iter() } /// Iterate over the symbols and their indices. /// /// This includes the null symbol at index 0, which you will usually need to skip. #[inline] pub fn enumerate(&self) -> impl Iterator { self.symbols .iter() .enumerate() .map(|(i, sym)| (SymbolIndex(i), sym)) } /// Return true if the symbol table is empty. #[inline] pub fn is_empty(&self) -> bool { self.symbols.is_empty() } /// The number of symbols. #[inline] pub fn len(&self) -> usize { self.symbols.len() } /// Get the symbol at the given index. /// /// Returns an error for null entry at index 0. pub fn symbol(&self, index: SymbolIndex) -> read::Result<&'data Elf::Sym> { if index == SymbolIndex(0) { return Err(read::Error("Invalid ELF symbol index")); } self.symbols .get(index.0) .read_error("Invalid ELF symbol index") } /// Return the extended section index for the given symbol if present. #[inline] pub fn shndx(&self, endian: Elf::Endian, index: SymbolIndex) -> Option { self.shndx.get(index.0).map(|x| x.get(endian)) } /// Return the section index for the given symbol. /// /// This uses the extended section index if present. pub fn symbol_section( &self, endian: Elf::Endian, symbol: &Elf::Sym, index: SymbolIndex, ) -> read::Result> { match symbol.st_shndx(endian) { elf::SHN_UNDEF => Ok(None), elf::SHN_XINDEX => { let shndx = self .shndx(endian, index) .read_error("Missing ELF symbol extended index")?; if shndx == 0 { Ok(None) } else { Ok(Some(SectionIndex(shndx as usize))) } } shndx if shndx < elf::SHN_LORESERVE => Ok(Some(SectionIndex(shndx.into()))), _ => Ok(None), } } /// Return the symbol name for the given symbol. pub fn symbol_name(&self, endian: Elf::Endian, symbol: &Elf::Sym) -> read::Result<&'data [u8]> { symbol.name(endian, self.strings) } /// Construct a map from addresses to a user-defined map entry. pub fn map Option>( &self, endian: Elf::Endian, f: F, ) -> SymbolMap { let mut symbols = Vec::with_capacity(self.symbols.len()); for symbol in self.symbols { if !symbol.is_definition(endian) { continue; } if let Some(entry) = f(symbol) { symbols.push(entry); } } SymbolMap::new(symbols) } } /// A symbol table in an [`ElfFile32`](super::ElfFile32). pub type ElfSymbolTable32<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfSymbolTable<'data, 'file, elf::FileHeader32, R>; /// A symbol table in an [`ElfFile32`](super::ElfFile32). pub type ElfSymbolTable64<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfSymbolTable<'data, 'file, elf::FileHeader64, R>; /// A symbol table in an [`ElfFile`](super::ElfFile). #[derive(Debug, Clone, Copy)] pub struct ElfSymbolTable<'data, 'file, Elf, R = &'data [u8]> where Elf: FileHeader, R: ReadRef<'data>, { pub(super) endian: Elf::Endian, pub(super) symbols: &'file SymbolTable<'data, Elf, R>, } impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> read::private::Sealed for ElfSymbolTable<'data, 'file, Elf, R> { } impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> ObjectSymbolTable<'data> for ElfSymbolTable<'data, 'file, Elf, R> { type Symbol = ElfSymbol<'data, 'file, Elf, R>; type SymbolIterator = ElfSymbolIterator<'data, 'file, Elf, R>; fn symbols(&self) -> Self::SymbolIterator { ElfSymbolIterator::new(self.endian, self.symbols) } fn symbol_by_index(&self, index: SymbolIndex) -> read::Result { let symbol = self.symbols.symbol(index)?; Ok(ElfSymbol { endian: self.endian, symbols: self.symbols, index, symbol, }) } } /// An iterator for the symbols in an [`ElfFile32`](super::ElfFile32). pub type ElfSymbolIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfSymbolIterator<'data, 'file, elf::FileHeader32, R>; /// An iterator for the symbols in an [`ElfFile64`](super::ElfFile64). pub type ElfSymbolIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfSymbolIterator<'data, 'file, elf::FileHeader64, R>; /// An iterator for the symbols in an [`ElfFile`](super::ElfFile). pub struct ElfSymbolIterator<'data, 'file, Elf, R = &'data [u8]> where Elf: FileHeader, R: ReadRef<'data>, { endian: Elf::Endian, symbols: &'file SymbolTable<'data, Elf, R>, index: SymbolIndex, } impl<'data, 'file, Elf, R> ElfSymbolIterator<'data, 'file, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { pub(super) fn new(endian: Elf::Endian, symbols: &'file SymbolTable<'data, Elf, R>) -> Self { ElfSymbolIterator { endian, symbols, index: SymbolIndex(1), } } } impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> fmt::Debug for ElfSymbolIterator<'data, 'file, Elf, R> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ElfSymbolIterator").finish() } } impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> Iterator for ElfSymbolIterator<'data, 'file, Elf, R> { type Item = ElfSymbol<'data, 'file, Elf, R>; fn next(&mut self) -> Option { let index = self.index; let symbol = self.symbols.symbols.get(index.0)?; self.index.0 += 1; Some(ElfSymbol { endian: self.endian, symbols: self.symbols, index, symbol, }) } } /// A symbol in an [`ElfFile32`](super::ElfFile32). pub type ElfSymbol32<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfSymbol<'data, 'file, elf::FileHeader32, R>; /// A symbol in an [`ElfFile64`](super::ElfFile64). pub type ElfSymbol64<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfSymbol<'data, 'file, elf::FileHeader64, R>; /// A symbol in an [`ElfFile`](super::ElfFile). /// /// Most functionality is provided by the [`ObjectSymbol`] trait implementation. #[derive(Debug, Clone, Copy)] pub struct ElfSymbol<'data, 'file, Elf, R = &'data [u8]> where Elf: FileHeader, R: ReadRef<'data>, { pub(super) endian: Elf::Endian, pub(super) symbols: &'file SymbolTable<'data, Elf, R>, pub(super) index: SymbolIndex, pub(super) symbol: &'data Elf::Sym, } impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> ElfSymbol<'data, 'file, Elf, R> { /// Get the endianness of the ELF file. pub fn endian(&self) -> Elf::Endian { self.endian } /// Return a reference to the raw symbol structure. #[inline] #[deprecated(note = "Use `elf_symbol` instead")] pub fn raw_symbol(&self) -> &'data Elf::Sym { self.symbol } /// Get the raw ELF symbol structure. pub fn elf_symbol(&self) -> &'data Elf::Sym { self.symbol } } impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> read::private::Sealed for ElfSymbol<'data, 'file, Elf, R> { } impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> ObjectSymbol<'data> for ElfSymbol<'data, 'file, Elf, R> { #[inline] fn index(&self) -> SymbolIndex { self.index } fn name_bytes(&self) -> read::Result<&'data [u8]> { self.symbol.name(self.endian, self.symbols.strings()) } fn name(&self) -> read::Result<&'data str> { let name = self.name_bytes()?; str::from_utf8(name) .ok() .read_error("Non UTF-8 ELF symbol name") } #[inline] fn address(&self) -> u64 { self.symbol.st_value(self.endian).into() } #[inline] fn size(&self) -> u64 { self.symbol.st_size(self.endian).into() } fn kind(&self) -> SymbolKind { match self.symbol.st_type() { elf::STT_NOTYPE => SymbolKind::Unknown, elf::STT_OBJECT | elf::STT_COMMON => SymbolKind::Data, elf::STT_FUNC | elf::STT_GNU_IFUNC => SymbolKind::Text, elf::STT_SECTION => SymbolKind::Section, elf::STT_FILE => SymbolKind::File, elf::STT_TLS => SymbolKind::Tls, _ => SymbolKind::Unknown, } } fn section(&self) -> SymbolSection { match self.symbol.st_shndx(self.endian) { elf::SHN_UNDEF => SymbolSection::Undefined, elf::SHN_ABS => { if self.symbol.st_type() == elf::STT_FILE { SymbolSection::None } else { SymbolSection::Absolute } } elf::SHN_COMMON => SymbolSection::Common, elf::SHN_XINDEX => match self.symbols.shndx(self.endian, self.index) { Some(0) => SymbolSection::None, Some(index) => SymbolSection::Section(SectionIndex(index as usize)), None => SymbolSection::Unknown, }, index if index < elf::SHN_LORESERVE => { SymbolSection::Section(SectionIndex(index as usize)) } _ => SymbolSection::Unknown, } } #[inline] fn is_undefined(&self) -> bool { self.symbol.is_undefined(self.endian) } #[inline] fn is_definition(&self) -> bool { self.symbol.is_definition(self.endian) } #[inline] fn is_common(&self) -> bool { self.symbol.is_common(self.endian) } #[inline] fn is_weak(&self) -> bool { self.symbol.is_weak() } fn scope(&self) -> SymbolScope { if self.symbol.st_shndx(self.endian) == elf::SHN_UNDEF { SymbolScope::Unknown } else { match self.symbol.st_bind() { elf::STB_LOCAL => SymbolScope::Compilation, elf::STB_GLOBAL | elf::STB_WEAK => { if self.symbol.st_visibility() == elf::STV_HIDDEN { SymbolScope::Linkage } else { SymbolScope::Dynamic } } _ => SymbolScope::Unknown, } } } #[inline] fn is_global(&self) -> bool { !self.symbol.is_local() } #[inline] fn is_local(&self) -> bool { self.symbol.is_local() } #[inline] fn flags(&self) -> SymbolFlags { SymbolFlags::Elf { st_info: self.symbol.st_info(), st_other: self.symbol.st_other(), } } } /// A trait for generic access to [`elf::Sym32`] and [`elf::Sym64`]. #[allow(missing_docs)] pub trait Sym: Debug + Pod { type Word: Into; type Endian: endian::Endian; fn st_name(&self, endian: Self::Endian) -> u32; fn st_info(&self) -> u8; fn st_bind(&self) -> u8; fn st_type(&self) -> u8; fn st_other(&self) -> u8; fn st_visibility(&self) -> u8; fn st_shndx(&self, endian: Self::Endian) -> u16; fn st_value(&self, endian: Self::Endian) -> Self::Word; fn st_size(&self, endian: Self::Endian) -> Self::Word; /// Parse the symbol name from the string table. fn name<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, strings: StringTable<'data, R>, ) -> read::Result<&'data [u8]> { strings .get(self.st_name(endian)) .read_error("Invalid ELF symbol name offset") } /// Return true if the symbol section is `SHN_UNDEF`. #[inline] fn is_undefined(&self, endian: Self::Endian) -> bool { self.st_shndx(endian) == elf::SHN_UNDEF } /// Return true if the symbol is a definition of a function or data object. fn is_definition(&self, endian: Self::Endian) -> bool { let shndx = self.st_shndx(endian); if shndx == elf::SHN_UNDEF || (shndx >= elf::SHN_LORESERVE && shndx != elf::SHN_XINDEX) { return false; } match self.st_type() { elf::STT_NOTYPE => self.st_size(endian).into() != 0, elf::STT_FUNC | elf::STT_OBJECT => true, _ => false, } } /// Return true if the symbol section is `SHN_COMMON`. fn is_common(&self, endian: Self::Endian) -> bool { self.st_shndx(endian) == elf::SHN_COMMON } /// Return true if the symbol section is `SHN_ABS`. fn is_absolute(&self, endian: Self::Endian) -> bool { self.st_shndx(endian) == elf::SHN_ABS } /// Return true if the symbol binding is `STB_LOCAL`. fn is_local(&self) -> bool { self.st_bind() == elf::STB_LOCAL } /// Return true if the symbol binding is `STB_WEAK`. fn is_weak(&self) -> bool { self.st_bind() == elf::STB_WEAK } } impl Sym for elf::Sym32 { type Word = u32; type Endian = Endian; #[inline] fn st_name(&self, endian: Self::Endian) -> u32 { self.st_name.get(endian) } #[inline] fn st_info(&self) -> u8 { self.st_info } #[inline] fn st_bind(&self) -> u8 { self.st_bind() } #[inline] fn st_type(&self) -> u8 { self.st_type() } #[inline] fn st_other(&self) -> u8 { self.st_other } #[inline] fn st_visibility(&self) -> u8 { self.st_visibility() } #[inline] fn st_shndx(&self, endian: Self::Endian) -> u16 { self.st_shndx.get(endian) } #[inline] fn st_value(&self, endian: Self::Endian) -> Self::Word { self.st_value.get(endian) } #[inline] fn st_size(&self, endian: Self::Endian) -> Self::Word { self.st_size.get(endian) } } impl Sym for elf::Sym64 { type Word = u64; type Endian = Endian; #[inline] fn st_name(&self, endian: Self::Endian) -> u32 { self.st_name.get(endian) } #[inline] fn st_info(&self) -> u8 { self.st_info } #[inline] fn st_bind(&self) -> u8 { self.st_bind() } #[inline] fn st_type(&self) -> u8 { self.st_type() } #[inline] fn st_other(&self) -> u8 { self.st_other } #[inline] fn st_visibility(&self) -> u8 { self.st_visibility() } #[inline] fn st_shndx(&self, endian: Self::Endian) -> u16 { self.st_shndx.get(endian) } #[inline] fn st_value(&self, endian: Self::Endian) -> Self::Word { self.st_value.get(endian) } #[inline] fn st_size(&self, endian: Self::Endian) -> Self::Word { self.st_size.get(endian) } } object-0.36.5/src/read/elf/version.rs000064400000000000000000000365671046102023000155110ustar 00000000000000use alloc::vec::Vec; use crate::read::{Bytes, ReadError, ReadRef, Result, StringTable, SymbolIndex}; use crate::{elf, endian}; use super::FileHeader; /// A version index. #[derive(Debug, Default, Clone, Copy)] pub struct VersionIndex(pub u16); impl VersionIndex { /// Return the version index. pub fn index(&self) -> u16 { self.0 & elf::VERSYM_VERSION } /// Return true if it is the local index. pub fn is_local(&self) -> bool { self.index() == elf::VER_NDX_LOCAL } /// Return true if it is the global index. pub fn is_global(&self) -> bool { self.index() == elf::VER_NDX_GLOBAL } /// Return the hidden flag. pub fn is_hidden(&self) -> bool { self.0 & elf::VERSYM_HIDDEN != 0 } } /// A version definition or requirement. /// /// This is derived from entries in the [`elf::SHT_GNU_VERDEF`] and [`elf::SHT_GNU_VERNEED`] sections. #[derive(Debug, Default, Clone, Copy)] pub struct Version<'data> { name: &'data [u8], hash: u32, // Used to keep track of valid indices in `VersionTable`. valid: bool, file: Option<&'data [u8]>, } impl<'data> Version<'data> { /// Return the version name. pub fn name(&self) -> &'data [u8] { self.name } /// Return hash of the version name. pub fn hash(&self) -> u32 { self.hash } /// Return the filename of the library containing this version. /// /// This is the `vn_file` field of the associated entry in [`elf::SHT_GNU_VERNEED`]. /// or `None` if the version info was parsed from a [`elf::SHT_GNU_VERDEF`] section. pub fn file(&self) -> Option<&'data [u8]> { self.file } } /// A table of version definitions and requirements. /// /// It allows looking up the version information for a given symbol index. /// /// This is derived from entries in the [`elf::SHT_GNU_VERSYM`], [`elf::SHT_GNU_VERDEF`] /// and [`elf::SHT_GNU_VERNEED`] sections. /// /// Returned by [`SectionTable::versions`](super::SectionTable::versions). #[derive(Debug, Clone)] pub struct VersionTable<'data, Elf: FileHeader> { symbols: &'data [elf::Versym], versions: Vec>, } impl<'data, Elf: FileHeader> Default for VersionTable<'data, Elf> { fn default() -> Self { VersionTable { symbols: &[], versions: Vec::new(), } } } impl<'data, Elf: FileHeader> VersionTable<'data, Elf> { /// Parse the version sections. pub fn parse>( endian: Elf::Endian, versyms: &'data [elf::Versym], verdefs: Option>, verneeds: Option>, strings: StringTable<'data, R>, ) -> Result { let mut max_index = 0; if let Some(mut verdefs) = verdefs.clone() { while let Some((verdef, _)) = verdefs.next()? { if verdef.vd_flags.get(endian) & elf::VER_FLG_BASE != 0 { continue; } let index = verdef.vd_ndx.get(endian) & elf::VERSYM_VERSION; if max_index < index { max_index = index; } } } if let Some(mut verneeds) = verneeds.clone() { while let Some((_, mut vernauxs)) = verneeds.next()? { while let Some(vernaux) = vernauxs.next()? { let index = vernaux.vna_other.get(endian) & elf::VERSYM_VERSION; if max_index < index { max_index = index; } } } } // Indices should be sequential, but this could be up to // 32k * size_of::() if max_index is bad. let mut versions = vec![Version::default(); max_index as usize + 1]; if let Some(mut verdefs) = verdefs { while let Some((verdef, mut verdauxs)) = verdefs.next()? { if verdef.vd_flags.get(endian) & elf::VER_FLG_BASE != 0 { continue; } let index = verdef.vd_ndx.get(endian) & elf::VERSYM_VERSION; if index <= elf::VER_NDX_GLOBAL { // TODO: return error? continue; } if let Some(verdaux) = verdauxs.next()? { versions[usize::from(index)] = Version { name: verdaux.name(endian, strings)?, hash: verdef.vd_hash.get(endian), valid: true, file: None, }; } } } if let Some(mut verneeds) = verneeds { while let Some((verneed, mut vernauxs)) = verneeds.next()? { while let Some(vernaux) = vernauxs.next()? { let index = vernaux.vna_other.get(endian) & elf::VERSYM_VERSION; if index <= elf::VER_NDX_GLOBAL { // TODO: return error? continue; } versions[usize::from(index)] = Version { name: vernaux.name(endian, strings)?, hash: vernaux.vna_hash.get(endian), valid: true, file: Some(verneed.file(endian, strings)?), }; } } } Ok(VersionTable { symbols: versyms, versions, }) } /// Return true if the version table is empty. pub fn is_empty(&self) -> bool { self.symbols.is_empty() } /// Return version index for a given symbol index. pub fn version_index(&self, endian: Elf::Endian, index: SymbolIndex) -> VersionIndex { let version_index = match self.symbols.get(index.0) { Some(x) => x.0.get(endian), // Ideally this would be VER_NDX_LOCAL for undefined symbols, // but currently there are no checks that need this distinction. None => elf::VER_NDX_GLOBAL, }; VersionIndex(version_index) } /// Return version information for a given symbol version index. /// /// Returns `Ok(None)` for local and global versions. /// Returns `Err(_)` if index is invalid. pub fn version(&self, index: VersionIndex) -> Result>> { if index.index() <= elf::VER_NDX_GLOBAL { return Ok(None); } self.versions .get(usize::from(index.index())) .filter(|version| version.valid) .read_error("Invalid ELF symbol version index") .map(Some) } /// Return true if the given symbol index satisfies the requirements of `need`. /// /// Returns false for any error. /// /// Note: this function hasn't been fully tested and is likely to be incomplete. pub fn matches( &self, endian: Elf::Endian, index: SymbolIndex, need: Option<&Version<'_>>, ) -> bool { let version_index = self.version_index(endian, index); let def = match self.version(version_index) { Ok(def) => def, Err(_) => return false, }; match (def, need) { (Some(def), Some(need)) => need.hash == def.hash && need.name == def.name, (None, Some(_need)) => { // Version must be present if needed. false } (Some(_def), None) => { // For a dlsym call, use the newest version. // TODO: if not a dlsym call, then use the oldest version. !version_index.is_hidden() } (None, None) => true, } } } /// An iterator for the entries in an ELF [`elf::SHT_GNU_VERDEF`] section. #[derive(Debug, Clone)] pub struct VerdefIterator<'data, Elf: FileHeader> { endian: Elf::Endian, data: Bytes<'data>, } impl<'data, Elf: FileHeader> VerdefIterator<'data, Elf> { pub(super) fn new(endian: Elf::Endian, data: &'data [u8]) -> Self { VerdefIterator { endian, data: Bytes(data), } } /// Return the next `Verdef` entry. pub fn next( &mut self, ) -> Result, VerdauxIterator<'data, Elf>)>> { if self.data.is_empty() { return Ok(None); } let result = self.parse().map(Some); if result.is_err() { self.data = Bytes(&[]); } result } fn parse(&mut self) -> Result<(&'data elf::Verdef, VerdauxIterator<'data, Elf>)> { let verdef = self .data .read_at::>(0) .read_error("ELF verdef is too short")?; let mut verdaux_data = self.data; verdaux_data .skip(verdef.vd_aux.get(self.endian) as usize) .read_error("Invalid ELF vd_aux")?; let verdaux = VerdauxIterator::new(self.endian, verdaux_data.0, verdef.vd_cnt.get(self.endian)); let next = verdef.vd_next.get(self.endian); if next != 0 { self.data .skip(next as usize) .read_error("Invalid ELF vd_next")?; } else { self.data = Bytes(&[]); } Ok((verdef, verdaux)) } } impl<'data, Elf: FileHeader> Iterator for VerdefIterator<'data, Elf> { type Item = Result<(&'data elf::Verdef, VerdauxIterator<'data, Elf>)>; fn next(&mut self) -> Option { self.next().transpose() } } /// An iterator for the auxiliary records for an entry in an ELF [`elf::SHT_GNU_VERDEF`] section. #[derive(Debug, Clone)] pub struct VerdauxIterator<'data, Elf: FileHeader> { endian: Elf::Endian, data: Bytes<'data>, count: u16, } impl<'data, Elf: FileHeader> VerdauxIterator<'data, Elf> { pub(super) fn new(endian: Elf::Endian, data: &'data [u8], count: u16) -> Self { VerdauxIterator { endian, data: Bytes(data), count, } } /// Return the next `Verdaux` entry. pub fn next(&mut self) -> Result>> { if self.count == 0 { return Ok(None); } let result = self.parse().map(Some); if result.is_err() { self.count = 0; } else { self.count -= 1; } result } fn parse(&mut self) -> Result<&'data elf::Verdaux> { let verdaux = self .data .read_at::>(0) .read_error("ELF verdaux is too short")?; self.data .skip(verdaux.vda_next.get(self.endian) as usize) .read_error("Invalid ELF vda_next")?; Ok(verdaux) } } impl<'data, Elf: FileHeader> Iterator for VerdauxIterator<'data, Elf> { type Item = Result<&'data elf::Verdaux>; fn next(&mut self) -> Option { self.next().transpose() } } /// An iterator for the entries in an ELF [`elf::SHT_GNU_VERNEED`] section. #[derive(Debug, Clone)] pub struct VerneedIterator<'data, Elf: FileHeader> { endian: Elf::Endian, data: Bytes<'data>, } impl<'data, Elf: FileHeader> VerneedIterator<'data, Elf> { pub(super) fn new(endian: Elf::Endian, data: &'data [u8]) -> Self { VerneedIterator { endian, data: Bytes(data), } } /// Return the next `Verneed` entry. pub fn next( &mut self, ) -> Result< Option<( &'data elf::Verneed, VernauxIterator<'data, Elf>, )>, > { if self.data.is_empty() { return Ok(None); } let result = self.parse().map(Some); if result.is_err() { self.data = Bytes(&[]); } result } fn parse( &mut self, ) -> Result<( &'data elf::Verneed, VernauxIterator<'data, Elf>, )> { let verneed = self .data .read_at::>(0) .read_error("ELF verneed is too short")?; let mut vernaux_data = self.data; vernaux_data .skip(verneed.vn_aux.get(self.endian) as usize) .read_error("Invalid ELF vn_aux")?; let vernaux = VernauxIterator::new(self.endian, vernaux_data.0, verneed.vn_cnt.get(self.endian)); let next = verneed.vn_next.get(self.endian); if next != 0 { self.data .skip(next as usize) .read_error("Invalid ELF vn_next")?; } else { self.data = Bytes(&[]); } Ok((verneed, vernaux)) } } impl<'data, Elf: FileHeader> Iterator for VerneedIterator<'data, Elf> { type Item = Result<( &'data elf::Verneed, VernauxIterator<'data, Elf>, )>; fn next(&mut self) -> Option { self.next().transpose() } } /// An iterator for the auxiliary records for an entry in an ELF [`elf::SHT_GNU_VERNEED`] section. #[derive(Debug, Clone)] pub struct VernauxIterator<'data, Elf: FileHeader> { endian: Elf::Endian, data: Bytes<'data>, count: u16, } impl<'data, Elf: FileHeader> VernauxIterator<'data, Elf> { pub(super) fn new(endian: Elf::Endian, data: &'data [u8], count: u16) -> Self { VernauxIterator { endian, data: Bytes(data), count, } } /// Return the next `Vernaux` entry. pub fn next(&mut self) -> Result>> { if self.count == 0 { return Ok(None); } let result = self.parse().map(Some); if result.is_err() { self.count = 0; } else { self.count -= 1; } result } fn parse(&mut self) -> Result<&'data elf::Vernaux> { let vernaux = self .data .read_at::>(0) .read_error("ELF vernaux is too short")?; self.data .skip(vernaux.vna_next.get(self.endian) as usize) .read_error("Invalid ELF vna_next")?; Ok(vernaux) } } impl<'data, Elf: FileHeader> Iterator for VernauxIterator<'data, Elf> { type Item = Result<&'data elf::Vernaux>; fn next(&mut self) -> Option { self.next().transpose() } } impl elf::Verdaux { /// Parse the version name from the string table. pub fn name<'data, R: ReadRef<'data>>( &self, endian: Endian, strings: StringTable<'data, R>, ) -> Result<&'data [u8]> { strings .get(self.vda_name.get(endian)) .read_error("Invalid ELF vda_name") } } impl elf::Verneed { /// Parse the file from the string table. pub fn file<'data, R: ReadRef<'data>>( &self, endian: Endian, strings: StringTable<'data, R>, ) -> Result<&'data [u8]> { strings .get(self.vn_file.get(endian)) .read_error("Invalid ELF vn_file") } } impl elf::Vernaux { /// Parse the version name from the string table. pub fn name<'data, R: ReadRef<'data>>( &self, endian: Endian, strings: StringTable<'data, R>, ) -> Result<&'data [u8]> { strings .get(self.vna_name.get(endian)) .read_error("Invalid ELF vna_name") } } object-0.36.5/src/read/gnu_compression.rs000064400000000000000000000027231046102023000164530ustar 00000000000000use crate::read::{self, Error, ReadError as _}; use crate::{endian, CompressedFileRange, CompressionFormat, ReadRef, U32Bytes}; // Attempt to parse the the CompressedFileRange for a section using the GNU-style // inline compression header format. This is used by the Go compiler in Mach-O files // as well as by the GNU linker in some ELF files. pub(super) fn compressed_file_range<'data, R: ReadRef<'data>>( file_data: R, section_offset: u64, section_size: u64, ) -> read::Result { let mut offset = section_offset; // Assume ZLIB-style uncompressed data is no more than 4GB to avoid accidentally // huge allocations. This also reduces the chance of accidentally matching on a // .debug_str that happens to start with "ZLIB". let header = file_data .read_bytes(&mut offset, 8) .read_error("GNU compressed section is too short")?; if header != b"ZLIB\0\0\0\0" { return Err(Error("Invalid GNU compressed section header")); } let uncompressed_size = file_data .read::>(&mut offset) .read_error("GNU compressed section is too short")? .get(endian::BigEndian) .into(); let compressed_size = section_size .checked_sub(offset - section_offset) .read_error("GNU compressed section is too short")?; Ok(CompressedFileRange { format: CompressionFormat::Zlib, offset, compressed_size, uncompressed_size, }) } object-0.36.5/src/read/macho/dyld_cache.rs000064400000000000000000000340021046102023000164020ustar 00000000000000use alloc::vec::Vec; use core::slice; use crate::endian::{Endian, Endianness}; use crate::macho; use crate::read::{Architecture, Error, File, ReadError, ReadRef, Result}; /// A parsed representation of the dyld shared cache. #[derive(Debug)] pub struct DyldCache<'data, E = Endianness, R = &'data [u8]> where E: Endian, R: ReadRef<'data>, { endian: E, data: R, subcaches: Vec>, mappings: &'data [macho::DyldCacheMappingInfo], images: &'data [macho::DyldCacheImageInfo], arch: Architecture, } /// Information about a subcache. #[derive(Debug)] pub struct DyldSubCache<'data, E = Endianness, R = &'data [u8]> where E: Endian, R: ReadRef<'data>, { data: R, mappings: &'data [macho::DyldCacheMappingInfo], } /// A slice of structs describing each subcache. The struct gained /// an additional field (the file suffix) in dyld-1042.1 (macOS 13 / iOS 16), /// so this is an enum of the two possible slice types. #[derive(Debug, Clone, Copy)] #[non_exhaustive] pub enum DyldSubCacheSlice<'data, E: Endian> { /// V1, used between dyld-940 and dyld-1042.1. V1(&'data [macho::DyldSubCacheEntryV1]), /// V2, used since dyld-1042.1. V2(&'data [macho::DyldSubCacheEntryV2]), } // This is the offset of the end of the images_across_all_subcaches_count field. const MIN_HEADER_SIZE_SUBCACHES_V1: u32 = 0x1c8; // This is the offset of the end of the cacheSubType field. // This field comes right after the images_across_all_subcaches_count field, // and we don't currently have it in our definition of the DyldCacheHeader type. const MIN_HEADER_SIZE_SUBCACHES_V2: u32 = 0x1d0; impl<'data, E, R> DyldCache<'data, E, R> where E: Endian, R: ReadRef<'data>, { /// Parse the raw dyld shared cache data. /// /// For shared caches from macOS 12 / iOS 15 and above, the subcache files need to be /// supplied as well, in the correct order, with the `.symbols` subcache last (if present). /// For example, `data` would be the data for `dyld_shared_cache_x86_64`, /// and `subcache_data` would be the data for `[dyld_shared_cache_x86_64.1, dyld_shared_cache_x86_64.2, ...]`. pub fn parse(data: R, subcache_data: &[R]) -> Result { let header = macho::DyldCacheHeader::parse(data)?; let (arch, endian) = header.parse_magic()?; let mappings = header.mappings(endian, data)?; let symbols_subcache_uuid = header.symbols_subcache_uuid(endian); let subcaches_info = header.subcaches(endian, data)?; let subcaches_count = match subcaches_info { Some(DyldSubCacheSlice::V1(subcaches)) => subcaches.len(), Some(DyldSubCacheSlice::V2(subcaches)) => subcaches.len(), None => 0, }; if subcache_data.len() != subcaches_count + symbols_subcache_uuid.is_some() as usize { return Err(Error("Incorrect number of SubCaches")); } // Split out the .symbols subcache data from the other subcaches. let (symbols_subcache_data_and_uuid, subcache_data) = if let Some(symbols_uuid) = symbols_subcache_uuid { let (sym_data, rest_data) = subcache_data.split_last().unwrap(); (Some((*sym_data, symbols_uuid)), rest_data) } else { (None, subcache_data) }; // Read the regular SubCaches, if present. let mut subcaches = Vec::new(); if let Some(subcaches_info) = subcaches_info { let (v1, v2) = match subcaches_info { DyldSubCacheSlice::V1(s) => (s, &[][..]), DyldSubCacheSlice::V2(s) => (&[][..], s), }; let uuids = v1.iter().map(|e| &e.uuid).chain(v2.iter().map(|e| &e.uuid)); for (&data, uuid) in subcache_data.iter().zip(uuids) { let sc_header = macho::DyldCacheHeader::::parse(data)?; if &sc_header.uuid != uuid { return Err(Error("Unexpected SubCache UUID")); } let mappings = sc_header.mappings(endian, data)?; subcaches.push(DyldSubCache { data, mappings }); } } // Read the .symbols SubCache, if present. // Other than the UUID verification, the symbols SubCache is currently unused. let _symbols_subcache = match symbols_subcache_data_and_uuid { Some((data, uuid)) => { let sc_header = macho::DyldCacheHeader::::parse(data)?; if sc_header.uuid != uuid { return Err(Error("Unexpected .symbols SubCache UUID")); } let mappings = sc_header.mappings(endian, data)?; Some(DyldSubCache { data, mappings }) } None => None, }; let images = header.images(endian, data)?; Ok(DyldCache { endian, data, subcaches, mappings, images, arch, }) } /// Get the architecture type of the file. pub fn architecture(&self) -> Architecture { self.arch } /// Get the endianness of the file. #[inline] pub fn endianness(&self) -> Endianness { if self.is_little_endian() { Endianness::Little } else { Endianness::Big } } /// Return true if the file is little endian, false if it is big endian. pub fn is_little_endian(&self) -> bool { self.endian.is_little_endian() } /// Iterate over the images in this cache. pub fn images<'cache>(&'cache self) -> DyldCacheImageIterator<'data, 'cache, E, R> { DyldCacheImageIterator { cache: self, iter: self.images.iter(), } } /// Find the address in a mapping and return the cache or subcache data it was found in, /// together with the translated file offset. pub fn data_and_offset_for_address(&self, address: u64) -> Option<(R, u64)> { if let Some(file_offset) = address_to_file_offset(address, self.endian, self.mappings) { return Some((self.data, file_offset)); } for subcache in &self.subcaches { if let Some(file_offset) = address_to_file_offset(address, self.endian, subcache.mappings) { return Some((subcache.data, file_offset)); } } None } } /// An iterator over all the images (dylibs) in the dyld shared cache. #[derive(Debug)] pub struct DyldCacheImageIterator<'data, 'cache, E = Endianness, R = &'data [u8]> where E: Endian, R: ReadRef<'data>, { cache: &'cache DyldCache<'data, E, R>, iter: slice::Iter<'data, macho::DyldCacheImageInfo>, } impl<'data, 'cache, E, R> Iterator for DyldCacheImageIterator<'data, 'cache, E, R> where E: Endian, R: ReadRef<'data>, { type Item = DyldCacheImage<'data, 'cache, E, R>; fn next(&mut self) -> Option> { let image_info = self.iter.next()?; Some(DyldCacheImage { cache: self.cache, image_info, }) } } /// One image (dylib) from inside the dyld shared cache. #[derive(Debug)] pub struct DyldCacheImage<'data, 'cache, E = Endianness, R = &'data [u8]> where E: Endian, R: ReadRef<'data>, { pub(crate) cache: &'cache DyldCache<'data, E, R>, image_info: &'data macho::DyldCacheImageInfo, } impl<'data, 'cache, E, R> DyldCacheImage<'data, 'cache, E, R> where E: Endian, R: ReadRef<'data>, { /// The file system path of this image. pub fn path(&self) -> Result<&'data str> { let path = self.image_info.path(self.cache.endian, self.cache.data)?; // The path should always be ascii, so from_utf8 should always succeed. let path = core::str::from_utf8(path).map_err(|_| Error("Path string not valid utf-8"))?; Ok(path) } /// The subcache data which contains the Mach-O header for this image, /// together with the file offset at which this image starts. pub fn image_data_and_offset(&self) -> Result<(R, u64)> { let address = self.image_info.address.get(self.cache.endian); self.cache .data_and_offset_for_address(address) .ok_or(Error("Address not found in any mapping")) } /// Parse this image into an Object. pub fn parse_object(&self) -> Result> { File::parse_dyld_cache_image(self) } } impl macho::DyldCacheHeader { /// Read the dyld cache header. pub fn parse<'data, R: ReadRef<'data>>(data: R) -> Result<&'data Self> { data.read_at::>(0) .read_error("Invalid dyld cache header size or alignment") } /// Returns (arch, endian) based on the magic string. pub fn parse_magic(&self) -> Result<(Architecture, E)> { let (arch, is_big_endian) = match &self.magic { b"dyld_v1 i386\0" => (Architecture::I386, false), b"dyld_v1 x86_64\0" => (Architecture::X86_64, false), b"dyld_v1 x86_64h\0" => (Architecture::X86_64, false), b"dyld_v1 ppc\0" => (Architecture::PowerPc, true), b"dyld_v1 armv6\0" => (Architecture::Arm, false), b"dyld_v1 armv7\0" => (Architecture::Arm, false), b"dyld_v1 armv7f\0" => (Architecture::Arm, false), b"dyld_v1 armv7s\0" => (Architecture::Arm, false), b"dyld_v1 armv7k\0" => (Architecture::Arm, false), b"dyld_v1 arm64\0" => (Architecture::Aarch64, false), b"dyld_v1 arm64e\0" => (Architecture::Aarch64, false), _ => return Err(Error("Unrecognized dyld cache magic")), }; let endian = E::from_big_endian(is_big_endian).read_error("Unsupported dyld cache endian")?; Ok((arch, endian)) } /// Return the mapping information table. pub fn mappings<'data, R: ReadRef<'data>>( &self, endian: E, data: R, ) -> Result<&'data [macho::DyldCacheMappingInfo]> { data.read_slice_at::>( self.mapping_offset.get(endian).into(), self.mapping_count.get(endian) as usize, ) .read_error("Invalid dyld cache mapping size or alignment") } /// Return the information about subcaches, if present. /// /// Returns `None` for dyld caches produced before dyld-940 (macOS 12). pub fn subcaches<'data, R: ReadRef<'data>>( &self, endian: E, data: R, ) -> Result>> { let header_size = self.mapping_offset.get(endian); if header_size >= MIN_HEADER_SIZE_SUBCACHES_V2 { let subcaches = data .read_slice_at::>( self.subcaches_offset.get(endian).into(), self.subcaches_count.get(endian) as usize, ) .read_error("Invalid dyld subcaches size or alignment")?; Ok(Some(DyldSubCacheSlice::V2(subcaches))) } else if header_size >= MIN_HEADER_SIZE_SUBCACHES_V1 { let subcaches = data .read_slice_at::>( self.subcaches_offset.get(endian).into(), self.subcaches_count.get(endian) as usize, ) .read_error("Invalid dyld subcaches size or alignment")?; Ok(Some(DyldSubCacheSlice::V1(subcaches))) } else { Ok(None) } } /// Return the UUID for the .symbols subcache, if present. pub fn symbols_subcache_uuid(&self, endian: E) -> Option<[u8; 16]> { if self.mapping_offset.get(endian) >= MIN_HEADER_SIZE_SUBCACHES_V1 { let uuid = self.symbols_subcache_uuid; if uuid != [0; 16] { return Some(uuid); } } None } /// Return the image information table. pub fn images<'data, R: ReadRef<'data>>( &self, endian: E, data: R, ) -> Result<&'data [macho::DyldCacheImageInfo]> { if self.mapping_offset.get(endian) >= MIN_HEADER_SIZE_SUBCACHES_V1 { data.read_slice_at::>( self.images_across_all_subcaches_offset.get(endian).into(), self.images_across_all_subcaches_count.get(endian) as usize, ) .read_error("Invalid dyld cache image size or alignment") } else { data.read_slice_at::>( self.images_offset.get(endian).into(), self.images_count.get(endian) as usize, ) .read_error("Invalid dyld cache image size or alignment") } } } impl macho::DyldCacheImageInfo { /// The file system path of this image. pub fn path<'data, R: ReadRef<'data>>(&self, endian: E, data: R) -> Result<&'data [u8]> { let r_start = self.path_file_offset.get(endian).into(); let r_end = data.len().read_error("Couldn't get data len()")?; data.read_bytes_at_until(r_start..r_end, 0) .read_error("Couldn't read dyld cache image path") } /// Find the file offset of the image by looking up its address in the mappings. pub fn file_offset( &self, endian: E, mappings: &[macho::DyldCacheMappingInfo], ) -> Result { let address = self.address.get(endian); address_to_file_offset(address, endian, mappings) .read_error("Invalid dyld cache image address") } } /// Find the file offset of the image by looking up its address in the mappings. pub fn address_to_file_offset( address: u64, endian: E, mappings: &[macho::DyldCacheMappingInfo], ) -> Option { for mapping in mappings { let mapping_address = mapping.address.get(endian); if address >= mapping_address && address < mapping_address.wrapping_add(mapping.size.get(endian)) { return Some(address - mapping_address + mapping.file_offset.get(endian)); } } None } object-0.36.5/src/read/macho/fat.rs000064400000000000000000000076411046102023000151060ustar 00000000000000use crate::endian::BigEndian; use crate::macho; use crate::pod::Pod; use crate::read::{Architecture, Error, ReadError, ReadRef, Result}; pub use macho::{FatArch32, FatArch64, FatHeader}; /// A 32-bit Mach-O universal binary. /// /// This is a file that starts with [`macho::FatHeader`], and corresponds /// to [`crate::FileKind::MachOFat32`]. pub type MachOFatFile32<'data> = MachOFatFile<'data, macho::FatArch32>; /// A 64-bit Mach-O universal binary. /// /// This is a file that starts with [`macho::FatHeader`], and corresponds /// to [`crate::FileKind::MachOFat64`]. pub type MachOFatFile64<'data> = MachOFatFile<'data, macho::FatArch64>; /// A Mach-O universal binary. /// /// This is a file that starts with [`macho::FatHeader`], and corresponds /// to [`crate::FileKind::MachOFat32`] or [`crate::FileKind::MachOFat64`]. #[derive(Debug, Clone)] pub struct MachOFatFile<'data, Fat: FatArch> { header: &'data macho::FatHeader, arches: &'data [Fat], } impl<'data, Fat: FatArch> MachOFatFile<'data, Fat> { /// Attempt to parse the fat header and fat arches. pub fn parse>(data: R) -> Result { let mut offset = 0; let header = data .read::(&mut offset) .read_error("Invalid fat header size or alignment")?; if header.magic.get(BigEndian) != Fat::MAGIC { return Err(Error("Invalid fat magic")); } let arches = data .read_slice::(&mut offset, header.nfat_arch.get(BigEndian) as usize) .read_error("Invalid nfat_arch")?; Ok(MachOFatFile { header, arches }) } /// Return the fat header pub fn header(&self) -> &'data macho::FatHeader { self.header } /// Return the array of fat arches. pub fn arches(&self) -> &'data [Fat] { self.arches } } /// A trait for generic access to [`macho::FatArch32`] and [`macho::FatArch64`]. #[allow(missing_docs)] pub trait FatArch: Pod { type Word: Into; const MAGIC: u32; fn cputype(&self) -> u32; fn cpusubtype(&self) -> u32; fn offset(&self) -> Self::Word; fn size(&self) -> Self::Word; fn align(&self) -> u32; fn architecture(&self) -> Architecture { match self.cputype() { macho::CPU_TYPE_ARM => Architecture::Arm, macho::CPU_TYPE_ARM64 => Architecture::Aarch64, macho::CPU_TYPE_X86 => Architecture::I386, macho::CPU_TYPE_X86_64 => Architecture::X86_64, macho::CPU_TYPE_MIPS => Architecture::Mips, macho::CPU_TYPE_POWERPC => Architecture::PowerPc, macho::CPU_TYPE_POWERPC64 => Architecture::PowerPc64, _ => Architecture::Unknown, } } fn file_range(&self) -> (u64, u64) { (self.offset().into(), self.size().into()) } fn data<'data, R: ReadRef<'data>>(&self, file: R) -> Result<&'data [u8]> { file.read_bytes_at(self.offset().into(), self.size().into()) .read_error("Invalid fat arch offset or size") } } impl FatArch for FatArch32 { type Word = u32; const MAGIC: u32 = macho::FAT_MAGIC; fn cputype(&self) -> u32 { self.cputype.get(BigEndian) } fn cpusubtype(&self) -> u32 { self.cpusubtype.get(BigEndian) } fn offset(&self) -> Self::Word { self.offset.get(BigEndian) } fn size(&self) -> Self::Word { self.size.get(BigEndian) } fn align(&self) -> u32 { self.align.get(BigEndian) } } impl FatArch for FatArch64 { type Word = u64; const MAGIC: u32 = macho::FAT_MAGIC_64; fn cputype(&self) -> u32 { self.cputype.get(BigEndian) } fn cpusubtype(&self) -> u32 { self.cpusubtype.get(BigEndian) } fn offset(&self) -> Self::Word { self.offset.get(BigEndian) } fn size(&self) -> Self::Word { self.size.get(BigEndian) } fn align(&self) -> u32 { self.align.get(BigEndian) } } object-0.36.5/src/read/macho/file.rs000064400000000000000000000635721046102023000152600ustar 00000000000000use alloc::vec::Vec; use core::fmt::Debug; use core::{mem, str}; use crate::endian::{self, BigEndian, Endian, Endianness}; use crate::macho; use crate::pod::Pod; use crate::read::{ self, Architecture, ByteString, ComdatKind, Error, Export, FileFlags, Import, NoDynamicRelocationIterator, Object, ObjectComdat, ObjectKind, ObjectMap, ObjectSection, ReadError, ReadRef, Result, SectionIndex, SubArchitecture, SymbolIndex, }; use super::{ DyldCacheImage, LoadCommandIterator, MachOSection, MachOSectionInternal, MachOSectionIterator, MachOSegment, MachOSegmentInternal, MachOSegmentIterator, MachOSymbol, MachOSymbolIterator, MachOSymbolTable, Nlist, Section, Segment, SymbolTable, }; /// A 32-bit Mach-O object file. /// /// This is a file that starts with [`macho::MachHeader32`], and corresponds /// to [`crate::FileKind::MachO32`]. pub type MachOFile32<'data, Endian = Endianness, R = &'data [u8]> = MachOFile<'data, macho::MachHeader32, R>; /// A 64-bit Mach-O object file. /// /// This is a file that starts with [`macho::MachHeader64`], and corresponds /// to [`crate::FileKind::MachO64`]. pub type MachOFile64<'data, Endian = Endianness, R = &'data [u8]> = MachOFile<'data, macho::MachHeader64, R>; /// A partially parsed Mach-O file. /// /// Most of the functionality of this type is provided by the [`Object`] trait implementation. #[derive(Debug)] pub struct MachOFile<'data, Mach, R = &'data [u8]> where Mach: MachHeader, R: ReadRef<'data>, { pub(super) endian: Mach::Endian, pub(super) data: R, pub(super) header_offset: u64, pub(super) header: &'data Mach, pub(super) segments: Vec>, pub(super) sections: Vec>, pub(super) symbols: SymbolTable<'data, Mach, R>, } impl<'data, Mach, R> MachOFile<'data, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { /// Parse the raw Mach-O file data. pub fn parse(data: R) -> Result { let header = Mach::parse(data, 0)?; let endian = header.endian()?; // Build a list of segments and sections to make some operations more efficient. let mut segments = Vec::new(); let mut sections = Vec::new(); let mut symbols = SymbolTable::default(); if let Ok(mut commands) = header.load_commands(endian, data, 0) { while let Ok(Some(command)) = commands.next() { if let Some((segment, section_data)) = Mach::Segment::from_command(command)? { segments.push(MachOSegmentInternal { segment, data }); for section in segment.sections(endian, section_data)? { let index = SectionIndex(sections.len() + 1); sections.push(MachOSectionInternal::parse(index, section, data)); } } else if let Some(symtab) = command.symtab()? { symbols = symtab.symbols(endian, data)?; } } } Ok(MachOFile { endian, data, header_offset: 0, header, segments, sections, symbols, }) } /// Parse the Mach-O file for the given image from the dyld shared cache. /// This will read different sections from different subcaches, if necessary. pub fn parse_dyld_cache_image<'cache, E: Endian>( image: &DyldCacheImage<'data, 'cache, E, R>, ) -> Result { let (data, header_offset) = image.image_data_and_offset()?; let header = Mach::parse(data, header_offset)?; let endian = header.endian()?; // Build a list of sections to make some operations more efficient. // Also build a list of segments, because we need to remember which ReadRef // to read each section's data from. Only the DyldCache knows this information, // and we won't have access to it once we've exited this function. let mut segments = Vec::new(); let mut sections = Vec::new(); let mut linkedit_data: Option = None; let mut symtab = None; if let Ok(mut commands) = header.load_commands(endian, data, header_offset) { while let Ok(Some(command)) = commands.next() { if let Some((segment, section_data)) = Mach::Segment::from_command(command)? { // Each segment can be stored in a different subcache. Get the segment's // address and look it up in the cache mappings, to find the correct cache data. // This was observed for the arm64e __LINKEDIT segment in macOS 12.0.1. let addr = segment.vmaddr(endian).into(); let (data, _offset) = image .cache .data_and_offset_for_address(addr) .read_error("Could not find segment data in dyld shared cache")?; if segment.name() == macho::SEG_LINKEDIT.as_bytes() { linkedit_data = Some(data); } segments.push(MachOSegmentInternal { segment, data }); for section in segment.sections(endian, section_data)? { let index = SectionIndex(sections.len() + 1); sections.push(MachOSectionInternal::parse(index, section, data)); } } else if let Some(st) = command.symtab()? { symtab = Some(st); } } } // The symbols are found in the __LINKEDIT segment, so make sure to read them from the // correct subcache. let symbols = match (symtab, linkedit_data) { (Some(symtab), Some(linkedit_data)) => symtab.symbols(endian, linkedit_data)?, _ => SymbolTable::default(), }; Ok(MachOFile { endian, data, header_offset, header, segments, sections, symbols, }) } /// Return the section at the given index. #[inline] pub(super) fn section_internal( &self, index: SectionIndex, ) -> Result<&MachOSectionInternal<'data, Mach, R>> { index .0 .checked_sub(1) .and_then(|index| self.sections.get(index)) .read_error("Invalid Mach-O section index") } /// Returns the endianness. pub fn endian(&self) -> Mach::Endian { self.endian } /// Returns the raw data. pub fn data(&self) -> R { self.data } /// Returns the raw Mach-O file header. #[deprecated(note = "Use `macho_header` instead")] pub fn raw_header(&self) -> &'data Mach { self.header } /// Get the raw Mach-O file header. pub fn macho_header(&self) -> &'data Mach { self.header } /// Get the Mach-O load commands. pub fn macho_load_commands(&self) -> Result> { self.header .load_commands(self.endian, self.data, self.header_offset) } /// Get the Mach-O symbol table. /// /// Returns an empty symbol table if the file has no symbol table. pub fn macho_symbol_table(&self) -> &SymbolTable<'data, Mach, R> { &self.symbols } /// Return the `LC_BUILD_VERSION` load command if present. pub fn build_version(&self) -> Result>> { let mut commands = self .header .load_commands(self.endian, self.data, self.header_offset)?; while let Some(command) = commands.next()? { if let Some(build_version) = command.build_version()? { return Ok(Some(build_version)); } } Ok(None) } } impl<'data, Mach, R> read::private::Sealed for MachOFile<'data, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { } impl<'data, Mach, R> Object<'data> for MachOFile<'data, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { type Segment<'file> = MachOSegment<'data, 'file, Mach, R> where Self: 'file, 'data: 'file; type SegmentIterator<'file> = MachOSegmentIterator<'data, 'file, Mach, R> where Self: 'file, 'data: 'file; type Section<'file> = MachOSection<'data, 'file, Mach, R> where Self: 'file, 'data: 'file; type SectionIterator<'file> = MachOSectionIterator<'data, 'file, Mach, R> where Self: 'file, 'data: 'file; type Comdat<'file> = MachOComdat<'data, 'file, Mach, R> where Self: 'file, 'data: 'file; type ComdatIterator<'file> = MachOComdatIterator<'data, 'file, Mach, R> where Self: 'file, 'data: 'file; type Symbol<'file> = MachOSymbol<'data, 'file, Mach, R> where Self: 'file, 'data: 'file; type SymbolIterator<'file> = MachOSymbolIterator<'data, 'file, Mach, R> where Self: 'file, 'data: 'file; type SymbolTable<'file> = MachOSymbolTable<'data, 'file, Mach, R> where Self: 'file, 'data: 'file; type DynamicRelocationIterator<'file> = NoDynamicRelocationIterator where Self: 'file, 'data: 'file; fn architecture(&self) -> Architecture { match self.header.cputype(self.endian) { macho::CPU_TYPE_ARM => Architecture::Arm, macho::CPU_TYPE_ARM64 => Architecture::Aarch64, macho::CPU_TYPE_ARM64_32 => Architecture::Aarch64_Ilp32, macho::CPU_TYPE_X86 => Architecture::I386, macho::CPU_TYPE_X86_64 => Architecture::X86_64, macho::CPU_TYPE_MIPS => Architecture::Mips, macho::CPU_TYPE_POWERPC => Architecture::PowerPc, macho::CPU_TYPE_POWERPC64 => Architecture::PowerPc64, _ => Architecture::Unknown, } } fn sub_architecture(&self) -> Option { match ( self.header.cputype(self.endian), self.header.cpusubtype(self.endian), ) { (macho::CPU_TYPE_ARM64, macho::CPU_SUBTYPE_ARM64E) => Some(SubArchitecture::Arm64E), _ => None, } } #[inline] fn is_little_endian(&self) -> bool { self.header.is_little_endian() } #[inline] fn is_64(&self) -> bool { self.header.is_type_64() } fn kind(&self) -> ObjectKind { match self.header.filetype(self.endian) { macho::MH_OBJECT => ObjectKind::Relocatable, macho::MH_EXECUTE => ObjectKind::Executable, macho::MH_CORE => ObjectKind::Core, macho::MH_DYLIB => ObjectKind::Dynamic, _ => ObjectKind::Unknown, } } fn segments(&self) -> MachOSegmentIterator<'data, '_, Mach, R> { MachOSegmentIterator { file: self, iter: self.segments.iter(), } } fn section_by_name_bytes<'file>( &'file self, section_name: &[u8], ) -> Option> { // Translate the section_name by stripping the query_prefix to construct // a function that matches names starting with name_prefix, taking into // consideration the maximum section name length. let make_prefix_matcher = |query_prefix: &'static [u8], name_prefix: &'static [u8]| { const MAX_SECTION_NAME_LEN: usize = 16; let suffix = section_name.strip_prefix(query_prefix).map(|suffix| { let max_len = MAX_SECTION_NAME_LEN - name_prefix.len(); &suffix[..suffix.len().min(max_len)] }); move |name: &[u8]| suffix.is_some() && name.strip_prefix(name_prefix) == suffix }; // Matches "__text" when searching for ".text" and "__debug_str_offs" // when searching for ".debug_str_offsets", as is common in // macOS/Mach-O. let matches_underscores_prefix = make_prefix_matcher(b".", b"__"); // Matches "__zdebug_info" when searching for ".debug_info" and // "__zdebug_str_off" when searching for ".debug_str_offsets", as is // used by Go when using GNU-style compression. let matches_zdebug_prefix = make_prefix_matcher(b".debug_", b"__zdebug_"); self.sections().find(|section| { section.name_bytes().map_or(false, |name| { name == section_name || matches_underscores_prefix(name) || matches_zdebug_prefix(name) }) }) } fn section_by_index(&self, index: SectionIndex) -> Result> { let internal = *self.section_internal(index)?; Ok(MachOSection { file: self, internal, }) } fn sections(&self) -> MachOSectionIterator<'data, '_, Mach, R> { MachOSectionIterator { file: self, iter: self.sections.iter(), } } fn comdats(&self) -> MachOComdatIterator<'data, '_, Mach, R> { MachOComdatIterator { file: self } } fn symbol_by_index(&self, index: SymbolIndex) -> Result> { let nlist = self.symbols.symbol(index)?; MachOSymbol::new(self, index, nlist).read_error("Unsupported Mach-O symbol index") } fn symbols(&self) -> MachOSymbolIterator<'data, '_, Mach, R> { MachOSymbolIterator::new(self) } #[inline] fn symbol_table(&self) -> Option> { Some(MachOSymbolTable { file: self }) } fn dynamic_symbols(&self) -> MachOSymbolIterator<'data, '_, Mach, R> { MachOSymbolIterator::empty(self) } #[inline] fn dynamic_symbol_table(&self) -> Option> { None } fn object_map(&self) -> ObjectMap<'data> { self.symbols.object_map(self.endian) } fn imports(&self) -> Result>> { let mut dysymtab = None; let mut libraries = Vec::new(); let twolevel = self.header.flags(self.endian) & macho::MH_TWOLEVEL != 0; if twolevel { libraries.push(&[][..]); } let mut commands = self .header .load_commands(self.endian, self.data, self.header_offset)?; while let Some(command) = commands.next()? { if let Some(command) = command.dysymtab()? { dysymtab = Some(command); } if twolevel { if let Some(dylib) = command.dylib()? { libraries.push(command.string(self.endian, dylib.dylib.name)?); } } } let mut imports = Vec::new(); if let Some(dysymtab) = dysymtab { let index = dysymtab.iundefsym.get(self.endian) as usize; let number = dysymtab.nundefsym.get(self.endian) as usize; for i in index..(index.wrapping_add(number)) { let symbol = self.symbols.symbol(SymbolIndex(i))?; let name = symbol.name(self.endian, self.symbols.strings())?; let library = if twolevel { libraries .get(symbol.library_ordinal(self.endian) as usize) .copied() .read_error("Invalid Mach-O symbol library ordinal")? } else { &[] }; imports.push(Import { name: ByteString(name), library: ByteString(library), }); } } Ok(imports) } fn exports(&self) -> Result>> { let mut dysymtab = None; let mut commands = self .header .load_commands(self.endian, self.data, self.header_offset)?; while let Some(command) = commands.next()? { if let Some(command) = command.dysymtab()? { dysymtab = Some(command); break; } } let mut exports = Vec::new(); if let Some(dysymtab) = dysymtab { let index = dysymtab.iextdefsym.get(self.endian) as usize; let number = dysymtab.nextdefsym.get(self.endian) as usize; for i in index..(index.wrapping_add(number)) { let symbol = self.symbols.symbol(SymbolIndex(i))?; let name = symbol.name(self.endian, self.symbols.strings())?; let address = symbol.n_value(self.endian).into(); exports.push(Export { name: ByteString(name), address, }); } } Ok(exports) } #[inline] fn dynamic_relocations(&self) -> Option { None } fn has_debug_symbols(&self) -> bool { self.section_by_name(".debug_info").is_some() } fn mach_uuid(&self) -> Result> { self.header.uuid(self.endian, self.data, self.header_offset) } fn relative_address_base(&self) -> u64 { 0 } fn entry(&self) -> u64 { if let Ok(mut commands) = self.header .load_commands(self.endian, self.data, self.header_offset) { while let Ok(Some(command)) = commands.next() { if let Ok(Some(command)) = command.entry_point() { return command.entryoff.get(self.endian); } } } 0 } fn flags(&self) -> FileFlags { FileFlags::MachO { flags: self.header.flags(self.endian), } } } /// An iterator for the COMDAT section groups in a [`MachOFile64`]. pub type MachOComdatIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = MachOComdatIterator<'data, 'file, macho::MachHeader32, R>; /// An iterator for the COMDAT section groups in a [`MachOFile64`]. pub type MachOComdatIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = MachOComdatIterator<'data, 'file, macho::MachHeader64, R>; /// An iterator for the COMDAT section groups in a [`MachOFile`]. /// /// This is a stub that doesn't implement any functionality. #[derive(Debug)] pub struct MachOComdatIterator<'data, 'file, Mach, R = &'data [u8]> where Mach: MachHeader, R: ReadRef<'data>, { #[allow(unused)] file: &'file MachOFile<'data, Mach, R>, } impl<'data, 'file, Mach, R> Iterator for MachOComdatIterator<'data, 'file, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { type Item = MachOComdat<'data, 'file, Mach, R>; #[inline] fn next(&mut self) -> Option { None } } /// A COMDAT section group in a [`MachOFile32`]. pub type MachOComdat32<'data, 'file, Endian = Endianness, R = &'data [u8]> = MachOComdat<'data, 'file, macho::MachHeader32, R>; /// A COMDAT section group in a [`MachOFile64`]. pub type MachOComdat64<'data, 'file, Endian = Endianness, R = &'data [u8]> = MachOComdat<'data, 'file, macho::MachHeader64, R>; /// A COMDAT section group in a [`MachOFile`]. /// /// This is a stub that doesn't implement any functionality. #[derive(Debug)] pub struct MachOComdat<'data, 'file, Mach, R = &'data [u8]> where Mach: MachHeader, R: ReadRef<'data>, { #[allow(unused)] file: &'file MachOFile<'data, Mach, R>, } impl<'data, 'file, Mach, R> read::private::Sealed for MachOComdat<'data, 'file, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { } impl<'data, 'file, Mach, R> ObjectComdat<'data> for MachOComdat<'data, 'file, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { type SectionIterator = MachOComdatSectionIterator<'data, 'file, Mach, R>; #[inline] fn kind(&self) -> ComdatKind { unreachable!(); } #[inline] fn symbol(&self) -> SymbolIndex { unreachable!(); } #[inline] fn name_bytes(&self) -> Result<&'data [u8]> { unreachable!(); } #[inline] fn name(&self) -> Result<&'data str> { unreachable!(); } #[inline] fn sections(&self) -> Self::SectionIterator { unreachable!(); } } /// An iterator for the sections in a COMDAT section group in a [`MachOFile32`]. pub type MachOComdatSectionIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = MachOComdatSectionIterator<'data, 'file, macho::MachHeader32, R>; /// An iterator for the sections in a COMDAT section group in a [`MachOFile64`]. pub type MachOComdatSectionIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = MachOComdatSectionIterator<'data, 'file, macho::MachHeader64, R>; /// An iterator for the sections in a COMDAT section group in a [`MachOFile`]. /// /// This is a stub that doesn't implement any functionality. #[derive(Debug)] pub struct MachOComdatSectionIterator<'data, 'file, Mach, R = &'data [u8]> where Mach: MachHeader, R: ReadRef<'data>, { #[allow(unused)] file: &'file MachOFile<'data, Mach, R>, } impl<'data, 'file, Mach, R> Iterator for MachOComdatSectionIterator<'data, 'file, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { type Item = SectionIndex; fn next(&mut self) -> Option { None } } /// A trait for generic access to [`macho::MachHeader32`] and [`macho::MachHeader64`]. #[allow(missing_docs)] pub trait MachHeader: Debug + Pod { type Word: Into; type Endian: endian::Endian; type Segment: Segment; type Section: Section; type Nlist: Nlist; /// Return true if this type is a 64-bit header. /// /// This is a property of the type, not a value in the header data. fn is_type_64(&self) -> bool; /// Return true if the `magic` field signifies big-endian. fn is_big_endian(&self) -> bool; /// Return true if the `magic` field signifies little-endian. fn is_little_endian(&self) -> bool; fn magic(&self) -> u32; fn cputype(&self, endian: Self::Endian) -> u32; fn cpusubtype(&self, endian: Self::Endian) -> u32; fn filetype(&self, endian: Self::Endian) -> u32; fn ncmds(&self, endian: Self::Endian) -> u32; fn sizeofcmds(&self, endian: Self::Endian) -> u32; fn flags(&self, endian: Self::Endian) -> u32; // Provided methods. /// Read the file header. /// /// Also checks that the magic field in the file header is a supported format. fn parse<'data, R: ReadRef<'data>>(data: R, offset: u64) -> read::Result<&'data Self> { let header = data .read_at::(offset) .read_error("Invalid Mach-O header size or alignment")?; if !header.is_supported() { return Err(Error("Unsupported Mach-O header")); } Ok(header) } fn is_supported(&self) -> bool { self.is_little_endian() || self.is_big_endian() } fn endian(&self) -> Result { Self::Endian::from_big_endian(self.is_big_endian()).read_error("Unsupported Mach-O endian") } fn load_commands<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, header_offset: u64, ) -> Result> { let data = data .read_bytes_at( header_offset + mem::size_of::() as u64, self.sizeofcmds(endian).into(), ) .read_error("Invalid Mach-O load command table size")?; Ok(LoadCommandIterator::new(endian, data, self.ncmds(endian))) } /// Return the UUID from the `LC_UUID` load command, if one is present. fn uuid<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, header_offset: u64, ) -> Result> { let mut commands = self.load_commands(endian, data, header_offset)?; while let Some(command) = commands.next()? { if let Ok(Some(uuid)) = command.uuid() { return Ok(Some(uuid.uuid)); } } Ok(None) } } impl MachHeader for macho::MachHeader32 { type Word = u32; type Endian = Endian; type Segment = macho::SegmentCommand32; type Section = macho::Section32; type Nlist = macho::Nlist32; fn is_type_64(&self) -> bool { false } fn is_big_endian(&self) -> bool { self.magic() == macho::MH_MAGIC } fn is_little_endian(&self) -> bool { self.magic() == macho::MH_CIGAM } fn magic(&self) -> u32 { self.magic.get(BigEndian) } fn cputype(&self, endian: Self::Endian) -> u32 { self.cputype.get(endian) } fn cpusubtype(&self, endian: Self::Endian) -> u32 { self.cpusubtype.get(endian) } fn filetype(&self, endian: Self::Endian) -> u32 { self.filetype.get(endian) } fn ncmds(&self, endian: Self::Endian) -> u32 { self.ncmds.get(endian) } fn sizeofcmds(&self, endian: Self::Endian) -> u32 { self.sizeofcmds.get(endian) } fn flags(&self, endian: Self::Endian) -> u32 { self.flags.get(endian) } } impl MachHeader for macho::MachHeader64 { type Word = u64; type Endian = Endian; type Segment = macho::SegmentCommand64; type Section = macho::Section64; type Nlist = macho::Nlist64; fn is_type_64(&self) -> bool { true } fn is_big_endian(&self) -> bool { self.magic() == macho::MH_MAGIC_64 } fn is_little_endian(&self) -> bool { self.magic() == macho::MH_CIGAM_64 } fn magic(&self) -> u32 { self.magic.get(BigEndian) } fn cputype(&self, endian: Self::Endian) -> u32 { self.cputype.get(endian) } fn cpusubtype(&self, endian: Self::Endian) -> u32 { self.cpusubtype.get(endian) } fn filetype(&self, endian: Self::Endian) -> u32 { self.filetype.get(endian) } fn ncmds(&self, endian: Self::Endian) -> u32 { self.ncmds.get(endian) } fn sizeofcmds(&self, endian: Self::Endian) -> u32 { self.sizeofcmds.get(endian) } fn flags(&self, endian: Self::Endian) -> u32 { self.flags.get(endian) } } object-0.36.5/src/read/macho/load_command.rs000064400000000000000000000364171046102023000167540ustar 00000000000000use core::marker::PhantomData; use core::mem; use crate::endian::Endian; use crate::macho; use crate::pod::Pod; use crate::read::macho::{MachHeader, SymbolTable}; use crate::read::{Bytes, Error, ReadError, ReadRef, Result, StringTable}; /// An iterator for the load commands from a [`MachHeader`]. #[derive(Debug, Default, Clone, Copy)] pub struct LoadCommandIterator<'data, E: Endian> { endian: E, data: Bytes<'data>, ncmds: u32, } impl<'data, E: Endian> LoadCommandIterator<'data, E> { pub(super) fn new(endian: E, data: &'data [u8], ncmds: u32) -> Self { LoadCommandIterator { endian, data: Bytes(data), ncmds, } } /// Return the next load command. pub fn next(&mut self) -> Result>> { if self.ncmds == 0 { return Ok(None); } let result = self.parse().map(Some); if result.is_err() { self.ncmds = 0; } else { self.ncmds -= 1; } result } fn parse(&mut self) -> Result> { let header = self .data .read_at::>(0) .read_error("Invalid Mach-O load command header")?; let cmd = header.cmd.get(self.endian); let cmdsize = header.cmdsize.get(self.endian) as usize; if cmdsize < mem::size_of::>() { return Err(Error("Invalid Mach-O load command size")); } let data = self .data .read_bytes(cmdsize) .read_error("Invalid Mach-O load command size")?; Ok(LoadCommandData { cmd, data, marker: Default::default(), }) } } impl<'data, E: Endian> Iterator for LoadCommandIterator<'data, E> { type Item = Result>; fn next(&mut self) -> Option { self.next().transpose() } } /// The data for a [`macho::LoadCommand`]. #[derive(Debug, Clone, Copy)] pub struct LoadCommandData<'data, E: Endian> { cmd: u32, // Includes the header. data: Bytes<'data>, marker: PhantomData, } impl<'data, E: Endian> LoadCommandData<'data, E> { /// Return the `cmd` field of the [`macho::LoadCommand`]. /// /// This is one of the `LC_` constants. pub fn cmd(&self) -> u32 { self.cmd } /// Return the `cmdsize` field of the [`macho::LoadCommand`]. pub fn cmdsize(&self) -> u32 { self.data.len() as u32 } /// Parse the data as the given type. #[inline] pub fn data(&self) -> Result<&'data T> { self.data .read_at(0) .read_error("Invalid Mach-O command size") } /// Raw bytes of this [`macho::LoadCommand`] structure. pub fn raw_data(&self) -> &'data [u8] { self.data.0 } /// Parse a load command string value. /// /// Strings used by load commands are specified by offsets that are /// relative to the load command header. pub fn string(&self, endian: E, s: macho::LcStr) -> Result<&'data [u8]> { self.data .read_string_at(s.offset.get(endian) as usize) .read_error("Invalid load command string offset") } /// Parse the command data according to the `cmd` field. pub fn variant(&self) -> Result> { Ok(match self.cmd { macho::LC_SEGMENT => { let mut data = self.data; let segment = data.read().read_error("Invalid Mach-O command size")?; LoadCommandVariant::Segment32(segment, data.0) } macho::LC_SYMTAB => LoadCommandVariant::Symtab(self.data()?), macho::LC_THREAD | macho::LC_UNIXTHREAD => { let mut data = self.data; let thread = data.read().read_error("Invalid Mach-O command size")?; LoadCommandVariant::Thread(thread, data.0) } macho::LC_DYSYMTAB => LoadCommandVariant::Dysymtab(self.data()?), macho::LC_LOAD_DYLIB | macho::LC_LOAD_WEAK_DYLIB | macho::LC_REEXPORT_DYLIB | macho::LC_LAZY_LOAD_DYLIB | macho::LC_LOAD_UPWARD_DYLIB => LoadCommandVariant::Dylib(self.data()?), macho::LC_ID_DYLIB => LoadCommandVariant::IdDylib(self.data()?), macho::LC_LOAD_DYLINKER => LoadCommandVariant::LoadDylinker(self.data()?), macho::LC_ID_DYLINKER => LoadCommandVariant::IdDylinker(self.data()?), macho::LC_PREBOUND_DYLIB => LoadCommandVariant::PreboundDylib(self.data()?), macho::LC_ROUTINES => LoadCommandVariant::Routines32(self.data()?), macho::LC_SUB_FRAMEWORK => LoadCommandVariant::SubFramework(self.data()?), macho::LC_SUB_UMBRELLA => LoadCommandVariant::SubUmbrella(self.data()?), macho::LC_SUB_CLIENT => LoadCommandVariant::SubClient(self.data()?), macho::LC_SUB_LIBRARY => LoadCommandVariant::SubLibrary(self.data()?), macho::LC_TWOLEVEL_HINTS => LoadCommandVariant::TwolevelHints(self.data()?), macho::LC_PREBIND_CKSUM => LoadCommandVariant::PrebindCksum(self.data()?), macho::LC_SEGMENT_64 => { let mut data = self.data; let segment = data.read().read_error("Invalid Mach-O command size")?; LoadCommandVariant::Segment64(segment, data.0) } macho::LC_ROUTINES_64 => LoadCommandVariant::Routines64(self.data()?), macho::LC_UUID => LoadCommandVariant::Uuid(self.data()?), macho::LC_RPATH => LoadCommandVariant::Rpath(self.data()?), macho::LC_CODE_SIGNATURE | macho::LC_SEGMENT_SPLIT_INFO | macho::LC_FUNCTION_STARTS | macho::LC_DATA_IN_CODE | macho::LC_DYLIB_CODE_SIGN_DRS | macho::LC_LINKER_OPTIMIZATION_HINT | macho::LC_DYLD_EXPORTS_TRIE | macho::LC_DYLD_CHAINED_FIXUPS => LoadCommandVariant::LinkeditData(self.data()?), macho::LC_ENCRYPTION_INFO => LoadCommandVariant::EncryptionInfo32(self.data()?), macho::LC_DYLD_INFO | macho::LC_DYLD_INFO_ONLY => { LoadCommandVariant::DyldInfo(self.data()?) } macho::LC_VERSION_MIN_MACOSX | macho::LC_VERSION_MIN_IPHONEOS | macho::LC_VERSION_MIN_TVOS | macho::LC_VERSION_MIN_WATCHOS => LoadCommandVariant::VersionMin(self.data()?), macho::LC_DYLD_ENVIRONMENT => LoadCommandVariant::DyldEnvironment(self.data()?), macho::LC_MAIN => LoadCommandVariant::EntryPoint(self.data()?), macho::LC_SOURCE_VERSION => LoadCommandVariant::SourceVersion(self.data()?), macho::LC_ENCRYPTION_INFO_64 => LoadCommandVariant::EncryptionInfo64(self.data()?), macho::LC_LINKER_OPTION => LoadCommandVariant::LinkerOption(self.data()?), macho::LC_NOTE => LoadCommandVariant::Note(self.data()?), macho::LC_BUILD_VERSION => LoadCommandVariant::BuildVersion(self.data()?), macho::LC_FILESET_ENTRY => LoadCommandVariant::FilesetEntry(self.data()?), _ => LoadCommandVariant::Other, }) } /// Try to parse this command as a [`macho::SegmentCommand32`]. /// /// Returns the segment command and the data containing the sections. pub fn segment_32(self) -> Result, &'data [u8])>> { if self.cmd == macho::LC_SEGMENT { let mut data = self.data; let segment = data.read().read_error("Invalid Mach-O command size")?; Ok(Some((segment, data.0))) } else { Ok(None) } } /// Try to parse this command as a [`macho::SymtabCommand`]. /// /// Returns the segment command and the data containing the sections. pub fn symtab(self) -> Result>> { if self.cmd == macho::LC_SYMTAB { Some(self.data()).transpose() } else { Ok(None) } } /// Try to parse this command as a [`macho::DysymtabCommand`]. pub fn dysymtab(self) -> Result>> { if self.cmd == macho::LC_DYSYMTAB { Some(self.data()).transpose() } else { Ok(None) } } /// Try to parse this command as a [`macho::DylibCommand`]. pub fn dylib(self) -> Result>> { if self.cmd == macho::LC_LOAD_DYLIB || self.cmd == macho::LC_LOAD_WEAK_DYLIB || self.cmd == macho::LC_REEXPORT_DYLIB || self.cmd == macho::LC_LAZY_LOAD_DYLIB || self.cmd == macho::LC_LOAD_UPWARD_DYLIB { Some(self.data()).transpose() } else { Ok(None) } } /// Try to parse this command as a [`macho::UuidCommand`]. pub fn uuid(self) -> Result>> { if self.cmd == macho::LC_UUID { Some(self.data()).transpose() } else { Ok(None) } } /// Try to parse this command as a [`macho::SegmentCommand64`]. pub fn segment_64(self) -> Result, &'data [u8])>> { if self.cmd == macho::LC_SEGMENT_64 { let mut data = self.data; let command = data.read().read_error("Invalid Mach-O command size")?; Ok(Some((command, data.0))) } else { Ok(None) } } /// Try to parse this command as a [`macho::DyldInfoCommand`]. pub fn dyld_info(self) -> Result>> { if self.cmd == macho::LC_DYLD_INFO || self.cmd == macho::LC_DYLD_INFO_ONLY { Some(self.data()).transpose() } else { Ok(None) } } /// Try to parse this command as an [`macho::EntryPointCommand`]. pub fn entry_point(self) -> Result>> { if self.cmd == macho::LC_MAIN { Some(self.data()).transpose() } else { Ok(None) } } /// Try to parse this command as a [`macho::BuildVersionCommand`]. pub fn build_version(self) -> Result>> { if self.cmd == macho::LC_BUILD_VERSION { Some(self.data()).transpose() } else { Ok(None) } } } /// A [`macho::LoadCommand`] that has been interpreted according to its `cmd` field. #[derive(Debug, Clone, Copy)] #[non_exhaustive] pub enum LoadCommandVariant<'data, E: Endian> { /// `LC_SEGMENT` Segment32(&'data macho::SegmentCommand32, &'data [u8]), /// `LC_SYMTAB` Symtab(&'data macho::SymtabCommand), // obsolete: `LC_SYMSEG` //Symseg(&'data macho::SymsegCommand), /// `LC_THREAD` or `LC_UNIXTHREAD` Thread(&'data macho::ThreadCommand, &'data [u8]), // obsolete: `LC_IDFVMLIB` or `LC_LOADFVMLIB` //Fvmlib(&'data macho::FvmlibCommand), // obsolete: `LC_IDENT` //Ident(&'data macho::IdentCommand), // internal: `LC_FVMFILE` //Fvmfile(&'data macho::FvmfileCommand), // internal: `LC_PREPAGE` /// `LC_DYSYMTAB` Dysymtab(&'data macho::DysymtabCommand), /// `LC_LOAD_DYLIB`, `LC_LOAD_WEAK_DYLIB`, `LC_REEXPORT_DYLIB`, /// `LC_LAZY_LOAD_DYLIB`, or `LC_LOAD_UPWARD_DYLIB` Dylib(&'data macho::DylibCommand), /// `LC_ID_DYLIB` IdDylib(&'data macho::DylibCommand), /// `LC_LOAD_DYLINKER` LoadDylinker(&'data macho::DylinkerCommand), /// `LC_ID_DYLINKER` IdDylinker(&'data macho::DylinkerCommand), /// `LC_PREBOUND_DYLIB` PreboundDylib(&'data macho::PreboundDylibCommand), /// `LC_ROUTINES` Routines32(&'data macho::RoutinesCommand32), /// `LC_SUB_FRAMEWORK` SubFramework(&'data macho::SubFrameworkCommand), /// `LC_SUB_UMBRELLA` SubUmbrella(&'data macho::SubUmbrellaCommand), /// `LC_SUB_CLIENT` SubClient(&'data macho::SubClientCommand), /// `LC_SUB_LIBRARY` SubLibrary(&'data macho::SubLibraryCommand), /// `LC_TWOLEVEL_HINTS` TwolevelHints(&'data macho::TwolevelHintsCommand), /// `LC_PREBIND_CKSUM` PrebindCksum(&'data macho::PrebindCksumCommand), /// `LC_SEGMENT_64` Segment64(&'data macho::SegmentCommand64, &'data [u8]), /// `LC_ROUTINES_64` Routines64(&'data macho::RoutinesCommand64), /// `LC_UUID` Uuid(&'data macho::UuidCommand), /// `LC_RPATH` Rpath(&'data macho::RpathCommand), /// `LC_CODE_SIGNATURE`, `LC_SEGMENT_SPLIT_INFO`, `LC_FUNCTION_STARTS`, /// `LC_DATA_IN_CODE`, `LC_DYLIB_CODE_SIGN_DRS`, `LC_LINKER_OPTIMIZATION_HINT`, /// `LC_DYLD_EXPORTS_TRIE`, or `LC_DYLD_CHAINED_FIXUPS`. LinkeditData(&'data macho::LinkeditDataCommand), /// `LC_ENCRYPTION_INFO` EncryptionInfo32(&'data macho::EncryptionInfoCommand32), /// `LC_DYLD_INFO` or `LC_DYLD_INFO_ONLY` DyldInfo(&'data macho::DyldInfoCommand), /// `LC_VERSION_MIN_MACOSX`, `LC_VERSION_MIN_IPHONEOS`, `LC_VERSION_MIN_WATCHOS`, /// or `LC_VERSION_MIN_TVOS` VersionMin(&'data macho::VersionMinCommand), /// `LC_DYLD_ENVIRONMENT` DyldEnvironment(&'data macho::DylinkerCommand), /// `LC_MAIN` EntryPoint(&'data macho::EntryPointCommand), /// `LC_SOURCE_VERSION` SourceVersion(&'data macho::SourceVersionCommand), /// `LC_ENCRYPTION_INFO_64` EncryptionInfo64(&'data macho::EncryptionInfoCommand64), /// `LC_LINKER_OPTION` LinkerOption(&'data macho::LinkerOptionCommand), /// `LC_NOTE` Note(&'data macho::NoteCommand), /// `LC_BUILD_VERSION` BuildVersion(&'data macho::BuildVersionCommand), /// `LC_FILESET_ENTRY` FilesetEntry(&'data macho::FilesetEntryCommand), /// An unrecognized or obsolete load command. Other, } impl macho::SymtabCommand { /// Return the symbol table that this command references. pub fn symbols<'data, Mach: MachHeader, R: ReadRef<'data>>( &self, endian: E, data: R, ) -> Result> { let symbols = data .read_slice_at( self.symoff.get(endian).into(), self.nsyms.get(endian) as usize, ) .read_error("Invalid Mach-O symbol table offset or size")?; let str_start: u64 = self.stroff.get(endian).into(); let str_end = str_start .checked_add(self.strsize.get(endian).into()) .read_error("Invalid Mach-O string table length")?; let strings = StringTable::new(data, str_start, str_end); Ok(SymbolTable::new(symbols, strings)) } } #[cfg(test)] mod tests { use super::*; use crate::LittleEndian; #[test] fn cmd_size_invalid() { #[repr(align(16))] struct Align([u8; N]); let mut commands = LoadCommandIterator::new(LittleEndian, &Align([0; 8]).0, 10); assert!(commands.next().is_err()); let mut commands = LoadCommandIterator::new(LittleEndian, &Align([0, 0, 0, 0, 7, 0, 0, 0, 0]).0, 10); assert!(commands.next().is_err()); let mut commands = LoadCommandIterator::new(LittleEndian, &Align([0, 0, 0, 0, 8, 0, 0, 0, 0]).0, 10); assert!(commands.next().is_ok()); } } object-0.36.5/src/read/macho/mod.rs000064400000000000000000000042141046102023000151040ustar 00000000000000//! Support for reading Mach-O files. //! //! Traits are used to abstract over the difference between 32-bit and 64-bit Mach-O //! files. The primary trait for this is [`MachHeader`]. //! //! ## High level API //! //! [`MachOFile`] implements the [`Object`](crate::read::Object) trait for Mach-O files. //! [`MachOFile`] is parameterised by [`MachHeader`] to allow reading both 32-bit and //! 64-bit Mach-O files. There are type aliases for these parameters ([`MachOFile32`] and //! [`MachOFile64`]). //! //! ## Low level API //! //! The [`MachHeader`] trait can be directly used to parse both [`macho::MachHeader32`] //! and [`macho::MachHeader64`]. Additionally, [`FatHeader`] and the [`FatArch`] trait //! can be used to iterate images in multi-architecture binaries, and [`DyldCache`] can //! be used to locate images in a dyld shared cache. //! //! ### Example for low level API //! ```no_run //! use object::macho; //! use object::read::macho::{MachHeader, Nlist}; //! use std::error::Error; //! use std::fs; //! //! /// Reads a file and displays the name of each symbol. //! fn main() -> Result<(), Box> { //! # #[cfg(feature = "std")] { //! let data = fs::read("path/to/binary")?; //! let header = macho::MachHeader64::::parse(&*data, 0)?; //! let endian = header.endian()?; //! let mut commands = header.load_commands(endian, &*data, 0)?; //! while let Some(command) = commands.next()? { //! if let Some(symtab_command) = command.symtab()? { //! let symbols = symtab_command.symbols::, _>(endian, &*data)?; //! for symbol in symbols.iter() { //! let name = symbol.name(endian, symbols.strings())?; //! println!("{}", String::from_utf8_lossy(name)); //! } //! } //! } //! # } //! Ok(()) //! } //! ``` #[cfg(doc)] use crate::macho; mod dyld_cache; pub use dyld_cache::*; mod fat; pub use fat::*; mod file; pub use file::*; mod load_command; pub use load_command::*; mod segment; pub use segment::*; mod section; pub use section::*; mod symbol; pub use symbol::*; mod relocation; pub use relocation::*; object-0.36.5/src/read/macho/relocation.rs000064400000000000000000000135051046102023000164670ustar 00000000000000use core::{fmt, slice}; use crate::endian::Endianness; use crate::macho; use crate::read::{ ReadRef, Relocation, RelocationEncoding, RelocationFlags, RelocationKind, RelocationTarget, SectionIndex, SymbolIndex, }; use super::{MachHeader, MachOFile}; /// An iterator for the relocations in a [`MachOSection32`](super::MachOSection32). pub type MachORelocationIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = MachORelocationIterator<'data, 'file, macho::MachHeader32, R>; /// An iterator for the relocations in a [`MachOSection64`](super::MachOSection64). pub type MachORelocationIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = MachORelocationIterator<'data, 'file, macho::MachHeader64, R>; /// An iterator for the relocations in a [`MachOSection`](super::MachOSection). pub struct MachORelocationIterator<'data, 'file, Mach, R = &'data [u8]> where Mach: MachHeader, R: ReadRef<'data>, { pub(super) file: &'file MachOFile<'data, Mach, R>, pub(super) relocations: slice::Iter<'data, macho::Relocation>, } impl<'data, 'file, Mach, R> Iterator for MachORelocationIterator<'data, 'file, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { type Item = (u64, Relocation); fn next(&mut self) -> Option { use RelocationEncoding as E; use RelocationKind as K; let mut paired_addend = 0; loop { let reloc = self.relocations.next()?; let endian = self.file.endian; let cputype = self.file.header.cputype(endian); if reloc.r_scattered(endian, cputype) { // FIXME: handle scattered relocations // We need to add `RelocationTarget::Address` for this. continue; } let reloc = reloc.info(self.file.endian); let flags = RelocationFlags::MachO { r_type: reloc.r_type, r_pcrel: reloc.r_pcrel, r_length: reloc.r_length, }; let g = E::Generic; let unknown = (K::Unknown, E::Generic); let (kind, encoding) = match cputype { macho::CPU_TYPE_ARM => match (reloc.r_type, reloc.r_pcrel) { (macho::ARM_RELOC_VANILLA, false) => (K::Absolute, g), _ => unknown, }, macho::CPU_TYPE_ARM64 | macho::CPU_TYPE_ARM64_32 => { match (reloc.r_type, reloc.r_pcrel) { (macho::ARM64_RELOC_UNSIGNED, false) => (K::Absolute, g), (macho::ARM64_RELOC_ADDEND, _) => { paired_addend = i64::from(reloc.r_symbolnum) .wrapping_shl(64 - 24) .wrapping_shr(64 - 24); continue; } _ => unknown, } } macho::CPU_TYPE_X86 => match (reloc.r_type, reloc.r_pcrel) { (macho::GENERIC_RELOC_VANILLA, false) => (K::Absolute, g), _ => unknown, }, macho::CPU_TYPE_X86_64 => match (reloc.r_type, reloc.r_pcrel) { (macho::X86_64_RELOC_UNSIGNED, false) => (K::Absolute, g), (macho::X86_64_RELOC_SIGNED, true) => (K::Relative, E::X86RipRelative), (macho::X86_64_RELOC_BRANCH, true) => (K::Relative, E::X86Branch), (macho::X86_64_RELOC_GOT, true) => (K::GotRelative, g), (macho::X86_64_RELOC_GOT_LOAD, true) => (K::GotRelative, E::X86RipRelativeMovq), _ => unknown, }, _ => unknown, }; let size = 8 << reloc.r_length; let target = if reloc.r_extern { RelocationTarget::Symbol(SymbolIndex(reloc.r_symbolnum as usize)) } else { RelocationTarget::Section(SectionIndex(reloc.r_symbolnum as usize)) }; let implicit_addend = paired_addend == 0; let mut addend = paired_addend; if reloc.r_pcrel { // For PC relative relocations on some architectures, the // addend does not include the offset required due to the // PC being different from the place of the relocation. // This differs from other file formats, so adjust the // addend here to account for this. match cputype { macho::CPU_TYPE_X86 => { addend -= 1 << reloc.r_length; } macho::CPU_TYPE_X86_64 => { addend -= 1 << reloc.r_length; match reloc.r_type { macho::X86_64_RELOC_SIGNED_1 => addend -= 1, macho::X86_64_RELOC_SIGNED_2 => addend -= 2, macho::X86_64_RELOC_SIGNED_4 => addend -= 4, _ => {} } } // TODO: maybe missing support for some architectures and relocations _ => {} } } return Some(( reloc.r_address as u64, Relocation { kind, encoding, size, target, addend, implicit_addend, flags, }, )); } } } impl<'data, 'file, Mach, R> fmt::Debug for MachORelocationIterator<'data, 'file, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("MachORelocationIterator").finish() } } object-0.36.5/src/read/macho/section.rs000064400000000000000000000317221046102023000157750ustar 00000000000000use core::fmt::Debug; use core::{fmt, result, slice, str}; use crate::endian::{self, Endianness}; use crate::macho; use crate::pod::Pod; use crate::read::{ self, gnu_compression, CompressedData, CompressedFileRange, ObjectSection, ReadError, ReadRef, RelocationMap, Result, SectionFlags, SectionIndex, SectionKind, }; use super::{MachHeader, MachOFile, MachORelocationIterator}; /// An iterator for the sections in a [`MachOFile32`](super::MachOFile32). pub type MachOSectionIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = MachOSectionIterator<'data, 'file, macho::MachHeader32, R>; /// An iterator for the sections in a [`MachOFile64`](super::MachOFile64). pub type MachOSectionIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = MachOSectionIterator<'data, 'file, macho::MachHeader64, R>; /// An iterator for the sections in a [`MachOFile`]. pub struct MachOSectionIterator<'data, 'file, Mach, R = &'data [u8]> where Mach: MachHeader, R: ReadRef<'data>, { pub(super) file: &'file MachOFile<'data, Mach, R>, pub(super) iter: slice::Iter<'file, MachOSectionInternal<'data, Mach, R>>, } impl<'data, 'file, Mach, R> fmt::Debug for MachOSectionIterator<'data, 'file, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // It's painful to do much better than this f.debug_struct("MachOSectionIterator").finish() } } impl<'data, 'file, Mach, R> Iterator for MachOSectionIterator<'data, 'file, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { type Item = MachOSection<'data, 'file, Mach, R>; fn next(&mut self) -> Option { self.iter.next().map(|&internal| MachOSection { file: self.file, internal, }) } } /// A section in a [`MachOFile32`](super::MachOFile32). pub type MachOSection32<'data, 'file, Endian = Endianness, R = &'data [u8]> = MachOSection<'data, 'file, macho::MachHeader32, R>; /// A section in a [`MachOFile64`](super::MachOFile64). pub type MachOSection64<'data, 'file, Endian = Endianness, R = &'data [u8]> = MachOSection<'data, 'file, macho::MachHeader64, R>; /// A section in a [`MachOFile`]. /// /// Most functionality is provided by the [`ObjectSection`] trait implementation. #[derive(Debug)] pub struct MachOSection<'data, 'file, Mach, R = &'data [u8]> where Mach: MachHeader, R: ReadRef<'data>, { pub(super) file: &'file MachOFile<'data, Mach, R>, pub(super) internal: MachOSectionInternal<'data, Mach, R>, } impl<'data, 'file, Mach, R> MachOSection<'data, 'file, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { /// Get the Mach-O file containing this section. pub fn macho_file(&self) -> &'file MachOFile<'data, Mach, R> { self.file } /// Get the raw Mach-O section structure. pub fn macho_section(&self) -> &'data Mach::Section { self.internal.section } /// Get the raw Mach-O relocation entries. pub fn macho_relocations(&self) -> Result<&'data [macho::Relocation]> { self.internal .section .relocations(self.file.endian, self.internal.data) } fn bytes(&self) -> Result<&'data [u8]> { self.internal .section .data(self.file.endian, self.internal.data) .read_error("Invalid Mach-O section size or offset") } // Try GNU-style "ZLIB" header decompression. fn maybe_compressed_gnu(&self) -> Result> { if !self .name() .map_or(false, |name| name.starts_with("__zdebug_")) { return Ok(None); } let (section_offset, section_size) = self .file_range() .read_error("Invalid ELF GNU compressed section type")?; gnu_compression::compressed_file_range(self.internal.data, section_offset, section_size) .map(Some) } } impl<'data, 'file, Mach, R> read::private::Sealed for MachOSection<'data, 'file, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { } impl<'data, 'file, Mach, R> ObjectSection<'data> for MachOSection<'data, 'file, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { type RelocationIterator = MachORelocationIterator<'data, 'file, Mach, R>; #[inline] fn index(&self) -> SectionIndex { self.internal.index } #[inline] fn address(&self) -> u64 { self.internal.section.addr(self.file.endian).into() } #[inline] fn size(&self) -> u64 { self.internal.section.size(self.file.endian).into() } #[inline] fn align(&self) -> u64 { let align = self.internal.section.align(self.file.endian); if align < 64 { 1 << align } else { 0 } } #[inline] fn file_range(&self) -> Option<(u64, u64)> { self.internal.section.file_range(self.file.endian) } #[inline] fn data(&self) -> Result<&'data [u8]> { self.bytes() } fn data_range(&self, address: u64, size: u64) -> Result> { Ok(read::util::data_range( self.bytes()?, self.address(), address, size, )) } fn compressed_file_range(&self) -> Result { Ok(if let Some(data) = self.maybe_compressed_gnu()? { data } else { CompressedFileRange::none(self.file_range()) }) } fn compressed_data(&self) -> read::Result> { self.compressed_file_range()?.data(self.file.data) } #[inline] fn name_bytes(&self) -> Result<&'data [u8]> { Ok(self.internal.section.name()) } #[inline] fn name(&self) -> Result<&'data str> { str::from_utf8(self.internal.section.name()) .ok() .read_error("Non UTF-8 Mach-O section name") } #[inline] fn segment_name_bytes(&self) -> Result> { Ok(Some(self.internal.section.segment_name())) } #[inline] fn segment_name(&self) -> Result> { Ok(Some( str::from_utf8(self.internal.section.segment_name()) .ok() .read_error("Non UTF-8 Mach-O segment name")?, )) } fn kind(&self) -> SectionKind { self.internal.kind } fn relocations(&self) -> MachORelocationIterator<'data, 'file, Mach, R> { MachORelocationIterator { file: self.file, relocations: self.macho_relocations().unwrap_or(&[]).iter(), } } fn relocation_map(&self) -> read::Result { RelocationMap::new(self.file, self) } fn flags(&self) -> SectionFlags { SectionFlags::MachO { flags: self.internal.section.flags(self.file.endian), } } } #[derive(Debug, Clone, Copy)] pub(super) struct MachOSectionInternal<'data, Mach: MachHeader, R: ReadRef<'data>> { pub index: SectionIndex, pub kind: SectionKind, pub section: &'data Mach::Section, /// The data for the file that contains the section data. /// /// This is required for dyld caches, where this may be a different subcache /// from the file containing the Mach-O load commands. pub data: R, } impl<'data, Mach: MachHeader, R: ReadRef<'data>> MachOSectionInternal<'data, Mach, R> { pub(super) fn parse(index: SectionIndex, section: &'data Mach::Section, data: R) -> Self { // TODO: we don't validate flags, should we? let kind = match (section.segment_name(), section.name()) { (b"__TEXT", b"__text") => SectionKind::Text, (b"__TEXT", b"__const") => SectionKind::ReadOnlyData, (b"__TEXT", b"__cstring") => SectionKind::ReadOnlyString, (b"__TEXT", b"__literal4") => SectionKind::ReadOnlyData, (b"__TEXT", b"__literal8") => SectionKind::ReadOnlyData, (b"__TEXT", b"__literal16") => SectionKind::ReadOnlyData, (b"__TEXT", b"__eh_frame") => SectionKind::ReadOnlyData, (b"__TEXT", b"__gcc_except_tab") => SectionKind::ReadOnlyData, (b"__DATA", b"__data") => SectionKind::Data, (b"__DATA", b"__const") => SectionKind::ReadOnlyData, (b"__DATA", b"__bss") => SectionKind::UninitializedData, (b"__DATA", b"__common") => SectionKind::Common, (b"__DATA", b"__thread_data") => SectionKind::Tls, (b"__DATA", b"__thread_bss") => SectionKind::UninitializedTls, (b"__DATA", b"__thread_vars") => SectionKind::TlsVariables, (b"__DWARF", _) => SectionKind::Debug, _ => SectionKind::Unknown, }; MachOSectionInternal { index, kind, section, data, } } } /// A trait for generic access to [`macho::Section32`] and [`macho::Section64`]. #[allow(missing_docs)] pub trait Section: Debug + Pod { type Word: Into; type Endian: endian::Endian; fn sectname(&self) -> &[u8; 16]; fn segname(&self) -> &[u8; 16]; fn addr(&self, endian: Self::Endian) -> Self::Word; fn size(&self, endian: Self::Endian) -> Self::Word; fn offset(&self, endian: Self::Endian) -> u32; fn align(&self, endian: Self::Endian) -> u32; fn reloff(&self, endian: Self::Endian) -> u32; fn nreloc(&self, endian: Self::Endian) -> u32; fn flags(&self, endian: Self::Endian) -> u32; /// Return the `sectname` bytes up until the null terminator. fn name(&self) -> &[u8] { let sectname = &self.sectname()[..]; match memchr::memchr(b'\0', sectname) { Some(end) => §name[..end], None => sectname, } } /// Return the `segname` bytes up until the null terminator. fn segment_name(&self) -> &[u8] { let segname = &self.segname()[..]; match memchr::memchr(b'\0', segname) { Some(end) => &segname[..end], None => segname, } } /// Return the offset and size of the section in the file. /// /// Returns `None` for sections that have no data in the file. fn file_range(&self, endian: Self::Endian) -> Option<(u64, u64)> { match self.flags(endian) & macho::SECTION_TYPE { macho::S_ZEROFILL | macho::S_GB_ZEROFILL | macho::S_THREAD_LOCAL_ZEROFILL => None, _ => Some((self.offset(endian).into(), self.size(endian).into())), } } /// Return the section data. /// /// Returns `Ok(&[])` if the section has no data. /// Returns `Err` for invalid values. fn data<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> result::Result<&'data [u8], ()> { if let Some((offset, size)) = self.file_range(endian) { data.read_bytes_at(offset, size) } else { Ok(&[]) } } /// Return the relocation array. /// /// Returns `Err` for invalid values. fn relocations<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> Result<&'data [macho::Relocation]> { data.read_slice_at(self.reloff(endian).into(), self.nreloc(endian) as usize) .read_error("Invalid Mach-O relocations offset or number") } } impl Section for macho::Section32 { type Word = u32; type Endian = Endian; fn sectname(&self) -> &[u8; 16] { &self.sectname } fn segname(&self) -> &[u8; 16] { &self.segname } fn addr(&self, endian: Self::Endian) -> Self::Word { self.addr.get(endian) } fn size(&self, endian: Self::Endian) -> Self::Word { self.size.get(endian) } fn offset(&self, endian: Self::Endian) -> u32 { self.offset.get(endian) } fn align(&self, endian: Self::Endian) -> u32 { self.align.get(endian) } fn reloff(&self, endian: Self::Endian) -> u32 { self.reloff.get(endian) } fn nreloc(&self, endian: Self::Endian) -> u32 { self.nreloc.get(endian) } fn flags(&self, endian: Self::Endian) -> u32 { self.flags.get(endian) } } impl Section for macho::Section64 { type Word = u64; type Endian = Endian; fn sectname(&self) -> &[u8; 16] { &self.sectname } fn segname(&self) -> &[u8; 16] { &self.segname } fn addr(&self, endian: Self::Endian) -> Self::Word { self.addr.get(endian) } fn size(&self, endian: Self::Endian) -> Self::Word { self.size.get(endian) } fn offset(&self, endian: Self::Endian) -> u32 { self.offset.get(endian) } fn align(&self, endian: Self::Endian) -> u32 { self.align.get(endian) } fn reloff(&self, endian: Self::Endian) -> u32 { self.reloff.get(endian) } fn nreloc(&self, endian: Self::Endian) -> u32 { self.nreloc.get(endian) } fn flags(&self, endian: Self::Endian) -> u32 { self.flags.get(endian) } } object-0.36.5/src/read/macho/segment.rs000064400000000000000000000230501046102023000157660ustar 00000000000000use core::fmt::Debug; use core::{result, slice, str}; use crate::endian::{self, Endianness}; use crate::macho; use crate::pod::Pod; use crate::read::{self, ObjectSegment, ReadError, ReadRef, Result, SegmentFlags}; use super::{LoadCommandData, MachHeader, MachOFile, Section}; /// An iterator for the segments in a [`MachOFile32`](super::MachOFile32). pub type MachOSegmentIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = MachOSegmentIterator<'data, 'file, macho::MachHeader32, R>; /// An iterator for the segments in a [`MachOFile64`](super::MachOFile64). pub type MachOSegmentIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = MachOSegmentIterator<'data, 'file, macho::MachHeader64, R>; /// An iterator for the segments in a [`MachOFile`]. #[derive(Debug)] pub struct MachOSegmentIterator<'data, 'file, Mach, R = &'data [u8]> where Mach: MachHeader, R: ReadRef<'data>, { pub(super) file: &'file MachOFile<'data, Mach, R>, pub(super) iter: slice::Iter<'file, MachOSegmentInternal<'data, Mach, R>>, } impl<'data, 'file, Mach, R> Iterator for MachOSegmentIterator<'data, 'file, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { type Item = MachOSegment<'data, 'file, Mach, R>; fn next(&mut self) -> Option { self.iter.next().map(|internal| MachOSegment { file: self.file, internal, }) } } /// A segment in a [`MachOFile32`](super::MachOFile32). pub type MachOSegment32<'data, 'file, Endian = Endianness, R = &'data [u8]> = MachOSegment<'data, 'file, macho::MachHeader32, R>; /// A segment in a [`MachOFile64`](super::MachOFile64). pub type MachOSegment64<'data, 'file, Endian = Endianness, R = &'data [u8]> = MachOSegment<'data, 'file, macho::MachHeader64, R>; /// A segment in a [`MachOFile`]. /// /// Most functionality is provided by the [`ObjectSegment`] trait implementation. #[derive(Debug)] pub struct MachOSegment<'data, 'file, Mach, R = &'data [u8]> where Mach: MachHeader, R: ReadRef<'data>, { file: &'file MachOFile<'data, Mach, R>, internal: &'file MachOSegmentInternal<'data, Mach, R>, } impl<'data, 'file, Mach, R> MachOSegment<'data, 'file, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { /// Get the Mach-O file containing this segment. pub fn macho_file(&self) -> &'file MachOFile<'data, Mach, R> { self.file } /// Get the raw Mach-O segment structure. pub fn macho_segment(&self) -> &'data Mach::Segment { self.internal.segment } fn bytes(&self) -> Result<&'data [u8]> { self.internal .segment .data(self.file.endian, self.internal.data) .read_error("Invalid Mach-O segment size or offset") } } impl<'data, 'file, Mach, R> read::private::Sealed for MachOSegment<'data, 'file, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { } impl<'data, 'file, Mach, R> ObjectSegment<'data> for MachOSegment<'data, 'file, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { #[inline] fn address(&self) -> u64 { self.internal.segment.vmaddr(self.file.endian).into() } #[inline] fn size(&self) -> u64 { self.internal.segment.vmsize(self.file.endian).into() } #[inline] fn align(&self) -> u64 { // Page size. 0x1000 } #[inline] fn file_range(&self) -> (u64, u64) { self.internal.segment.file_range(self.file.endian) } fn data(&self) -> Result<&'data [u8]> { self.bytes() } fn data_range(&self, address: u64, size: u64) -> Result> { Ok(read::util::data_range( self.bytes()?, self.address(), address, size, )) } #[inline] fn name_bytes(&self) -> Result> { Ok(Some(self.internal.segment.name())) } #[inline] fn name(&self) -> Result> { Ok(Some( str::from_utf8(self.internal.segment.name()) .ok() .read_error("Non UTF-8 Mach-O segment name")?, )) } #[inline] fn flags(&self) -> SegmentFlags { let flags = self.internal.segment.flags(self.file.endian); let maxprot = self.internal.segment.maxprot(self.file.endian); let initprot = self.internal.segment.initprot(self.file.endian); SegmentFlags::MachO { flags, maxprot, initprot, } } } #[derive(Debug, Clone, Copy)] pub(super) struct MachOSegmentInternal<'data, Mach: MachHeader, R: ReadRef<'data>> { pub segment: &'data Mach::Segment, /// The data for the file that contains the segment data. /// /// This is required for dyld caches, where this may be a different subcache /// from the file containing the Mach-O load commands. pub data: R, } /// A trait for generic access to [`macho::SegmentCommand32`] and [`macho::SegmentCommand64`]. #[allow(missing_docs)] pub trait Segment: Debug + Pod { type Word: Into; type Endian: endian::Endian; type Section: Section; fn from_command(command: LoadCommandData<'_, Self::Endian>) -> Result>; fn cmd(&self, endian: Self::Endian) -> u32; fn cmdsize(&self, endian: Self::Endian) -> u32; fn segname(&self) -> &[u8; 16]; fn vmaddr(&self, endian: Self::Endian) -> Self::Word; fn vmsize(&self, endian: Self::Endian) -> Self::Word; fn fileoff(&self, endian: Self::Endian) -> Self::Word; fn filesize(&self, endian: Self::Endian) -> Self::Word; fn maxprot(&self, endian: Self::Endian) -> u32; fn initprot(&self, endian: Self::Endian) -> u32; fn nsects(&self, endian: Self::Endian) -> u32; fn flags(&self, endian: Self::Endian) -> u32; /// Return the `segname` bytes up until the null terminator. fn name(&self) -> &[u8] { let segname = &self.segname()[..]; match memchr::memchr(b'\0', segname) { Some(end) => &segname[..end], None => segname, } } /// Return the offset and size of the segment in the file. fn file_range(&self, endian: Self::Endian) -> (u64, u64) { (self.fileoff(endian).into(), self.filesize(endian).into()) } /// Get the segment data from the file data. /// /// Returns `Err` for invalid values. fn data<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> result::Result<&'data [u8], ()> { let (offset, size) = self.file_range(endian); data.read_bytes_at(offset, size) } /// Get the array of sections from the data following the segment command. /// /// Returns `Err` for invalid values. fn sections<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, section_data: R, ) -> Result<&'data [Self::Section]> { section_data .read_slice_at(0, self.nsects(endian) as usize) .read_error("Invalid Mach-O number of sections") } } impl Segment for macho::SegmentCommand32 { type Word = u32; type Endian = Endian; type Section = macho::Section32; fn from_command(command: LoadCommandData<'_, Self::Endian>) -> Result> { command.segment_32() } fn cmd(&self, endian: Self::Endian) -> u32 { self.cmd.get(endian) } fn cmdsize(&self, endian: Self::Endian) -> u32 { self.cmdsize.get(endian) } fn segname(&self) -> &[u8; 16] { &self.segname } fn vmaddr(&self, endian: Self::Endian) -> Self::Word { self.vmaddr.get(endian) } fn vmsize(&self, endian: Self::Endian) -> Self::Word { self.vmsize.get(endian) } fn fileoff(&self, endian: Self::Endian) -> Self::Word { self.fileoff.get(endian) } fn filesize(&self, endian: Self::Endian) -> Self::Word { self.filesize.get(endian) } fn maxprot(&self, endian: Self::Endian) -> u32 { self.maxprot.get(endian) } fn initprot(&self, endian: Self::Endian) -> u32 { self.initprot.get(endian) } fn nsects(&self, endian: Self::Endian) -> u32 { self.nsects.get(endian) } fn flags(&self, endian: Self::Endian) -> u32 { self.flags.get(endian) } } impl Segment for macho::SegmentCommand64 { type Word = u64; type Endian = Endian; type Section = macho::Section64; fn from_command(command: LoadCommandData<'_, Self::Endian>) -> Result> { command.segment_64() } fn cmd(&self, endian: Self::Endian) -> u32 { self.cmd.get(endian) } fn cmdsize(&self, endian: Self::Endian) -> u32 { self.cmdsize.get(endian) } fn segname(&self) -> &[u8; 16] { &self.segname } fn vmaddr(&self, endian: Self::Endian) -> Self::Word { self.vmaddr.get(endian) } fn vmsize(&self, endian: Self::Endian) -> Self::Word { self.vmsize.get(endian) } fn fileoff(&self, endian: Self::Endian) -> Self::Word { self.fileoff.get(endian) } fn filesize(&self, endian: Self::Endian) -> Self::Word { self.filesize.get(endian) } fn maxprot(&self, endian: Self::Endian) -> u32 { self.maxprot.get(endian) } fn initprot(&self, endian: Self::Endian) -> u32 { self.initprot.get(endian) } fn nsects(&self, endian: Self::Endian) -> u32 { self.nsects.get(endian) } fn flags(&self, endian: Self::Endian) -> u32 { self.flags.get(endian) } } object-0.36.5/src/read/macho/symbol.rs000064400000000000000000000401531046102023000156340ustar 00000000000000use alloc::vec::Vec; use core::fmt::Debug; use core::{fmt, slice, str}; use crate::endian::{self, Endianness}; use crate::macho; use crate::pod::Pod; use crate::read::util::StringTable; use crate::read::{ self, ObjectMap, ObjectMapEntry, ObjectMapFile, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, Result, SectionIndex, SectionKind, SymbolFlags, SymbolIndex, SymbolKind, SymbolMap, SymbolMapEntry, SymbolScope, SymbolSection, }; use super::{MachHeader, MachOFile}; /// A table of symbol entries in a Mach-O file. /// /// Also includes the string table used for the symbol names. /// /// Returned by [`macho::SymtabCommand::symbols`]. #[derive(Debug, Clone, Copy)] pub struct SymbolTable<'data, Mach: MachHeader, R = &'data [u8]> where R: ReadRef<'data>, { symbols: &'data [Mach::Nlist], strings: StringTable<'data, R>, } impl<'data, Mach: MachHeader, R: ReadRef<'data>> Default for SymbolTable<'data, Mach, R> { fn default() -> Self { SymbolTable { symbols: &[], strings: Default::default(), } } } impl<'data, Mach: MachHeader, R: ReadRef<'data>> SymbolTable<'data, Mach, R> { #[inline] pub(super) fn new(symbols: &'data [Mach::Nlist], strings: StringTable<'data, R>) -> Self { SymbolTable { symbols, strings } } /// Return the string table used for the symbol names. #[inline] pub fn strings(&self) -> StringTable<'data, R> { self.strings } /// Iterate over the symbols. #[inline] pub fn iter(&self) -> slice::Iter<'data, Mach::Nlist> { self.symbols.iter() } /// Return true if the symbol table is empty. #[inline] pub fn is_empty(&self) -> bool { self.symbols.is_empty() } /// The number of symbols. #[inline] pub fn len(&self) -> usize { self.symbols.len() } /// Return the symbol at the given index. pub fn symbol(&self, index: SymbolIndex) -> Result<&'data Mach::Nlist> { self.symbols .get(index.0) .read_error("Invalid Mach-O symbol index") } /// Construct a map from addresses to a user-defined map entry. pub fn map Option>( &self, f: F, ) -> SymbolMap { let mut symbols = Vec::new(); for nlist in self.symbols { if !nlist.is_definition() { continue; } if let Some(entry) = f(nlist) { symbols.push(entry); } } SymbolMap::new(symbols) } /// Construct a map from addresses to symbol names and object file names. pub fn object_map(&self, endian: Mach::Endian) -> ObjectMap<'data> { let mut symbols = Vec::new(); let mut objects = Vec::new(); let mut object = None; let mut current_function = None; // Each module starts with one or two N_SO symbols (path, or directory + filename) // and one N_OSO symbol. The module is terminated by an empty N_SO symbol. for nlist in self.symbols { let n_type = nlist.n_type(); if n_type & macho::N_STAB == 0 { continue; } // TODO: includes variables too (N_GSYM, N_STSYM). These may need to get their // address from regular symbols though. match n_type { macho::N_SO => { object = None; } macho::N_OSO => { object = None; if let Ok(name) = nlist.name(endian, self.strings) { if !name.is_empty() { object = Some(objects.len()); // `N_OSO` symbol names can be either `/path/to/object.o` // or `/path/to/archive.a(object.o)`. let (path, member) = name .split_last() .and_then(|(last, head)| { if *last != b')' { return None; } let index = head.iter().position(|&x| x == b'(')?; let (archive, rest) = head.split_at(index); Some((archive, Some(&rest[1..]))) }) .unwrap_or((name, None)); objects.push(ObjectMapFile::new(path, member)); } } } macho::N_FUN => { if let Ok(name) = nlist.name(endian, self.strings) { if !name.is_empty() { current_function = Some((name, nlist.n_value(endian).into())) } else if let Some((name, address)) = current_function.take() { if let Some(object) = object { symbols.push(ObjectMapEntry { address, size: nlist.n_value(endian).into(), name, object, }); } } } } _ => {} } } ObjectMap { symbols: SymbolMap::new(symbols), objects, } } } /// A symbol table in a [`MachOFile32`](super::MachOFile32). pub type MachOSymbolTable32<'data, 'file, Endian = Endianness, R = &'data [u8]> = MachOSymbolTable<'data, 'file, macho::MachHeader32, R>; /// A symbol table in a [`MachOFile64`](super::MachOFile64). pub type MachOSymbolTable64<'data, 'file, Endian = Endianness, R = &'data [u8]> = MachOSymbolTable<'data, 'file, macho::MachHeader64, R>; /// A symbol table in a [`MachOFile`]. #[derive(Debug, Clone, Copy)] pub struct MachOSymbolTable<'data, 'file, Mach, R = &'data [u8]> where Mach: MachHeader, R: ReadRef<'data>, { pub(super) file: &'file MachOFile<'data, Mach, R>, } impl<'data, 'file, Mach, R> read::private::Sealed for MachOSymbolTable<'data, 'file, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { } impl<'data, 'file, Mach, R> ObjectSymbolTable<'data> for MachOSymbolTable<'data, 'file, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { type Symbol = MachOSymbol<'data, 'file, Mach, R>; type SymbolIterator = MachOSymbolIterator<'data, 'file, Mach, R>; fn symbols(&self) -> Self::SymbolIterator { MachOSymbolIterator::new(self.file) } fn symbol_by_index(&self, index: SymbolIndex) -> Result { let nlist = self.file.symbols.symbol(index)?; MachOSymbol::new(self.file, index, nlist).read_error("Unsupported Mach-O symbol index") } } /// An iterator for the symbols in a [`MachOFile32`](super::MachOFile32). pub type MachOSymbolIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = MachOSymbolIterator<'data, 'file, macho::MachHeader32, R>; /// An iterator for the symbols in a [`MachOFile64`](super::MachOFile64). pub type MachOSymbolIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = MachOSymbolIterator<'data, 'file, macho::MachHeader64, R>; /// An iterator for the symbols in a [`MachOFile`]. pub struct MachOSymbolIterator<'data, 'file, Mach, R = &'data [u8]> where Mach: MachHeader, R: ReadRef<'data>, { file: &'file MachOFile<'data, Mach, R>, index: SymbolIndex, } impl<'data, 'file, Mach, R> MachOSymbolIterator<'data, 'file, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { pub(super) fn new(file: &'file MachOFile<'data, Mach, R>) -> Self { MachOSymbolIterator { file, index: SymbolIndex(0), } } pub(super) fn empty(file: &'file MachOFile<'data, Mach, R>) -> Self { MachOSymbolIterator { file, index: SymbolIndex(file.symbols.len()), } } } impl<'data, 'file, Mach, R> fmt::Debug for MachOSymbolIterator<'data, 'file, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("MachOSymbolIterator").finish() } } impl<'data, 'file, Mach, R> Iterator for MachOSymbolIterator<'data, 'file, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { type Item = MachOSymbol<'data, 'file, Mach, R>; fn next(&mut self) -> Option { loop { let index = self.index; let nlist = self.file.symbols.symbols.get(index.0)?; self.index.0 += 1; if let Some(symbol) = MachOSymbol::new(self.file, index, nlist) { return Some(symbol); } } } } /// A symbol in a [`MachOFile32`](super::MachOFile32). pub type MachOSymbol32<'data, 'file, Endian = Endianness, R = &'data [u8]> = MachOSymbol<'data, 'file, macho::MachHeader32, R>; /// A symbol in a [`MachOFile64`](super::MachOFile64). pub type MachOSymbol64<'data, 'file, Endian = Endianness, R = &'data [u8]> = MachOSymbol<'data, 'file, macho::MachHeader64, R>; /// A symbol in a [`MachOFile`]. /// /// Most functionality is provided by the [`ObjectSymbol`] trait implementation. #[derive(Debug, Clone, Copy)] pub struct MachOSymbol<'data, 'file, Mach, R = &'data [u8]> where Mach: MachHeader, R: ReadRef<'data>, { file: &'file MachOFile<'data, Mach, R>, index: SymbolIndex, nlist: &'data Mach::Nlist, } impl<'data, 'file, Mach, R> MachOSymbol<'data, 'file, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { pub(super) fn new( file: &'file MachOFile<'data, Mach, R>, index: SymbolIndex, nlist: &'data Mach::Nlist, ) -> Option { if nlist.n_type() & macho::N_STAB != 0 { return None; } Some(MachOSymbol { file, index, nlist }) } /// Get the Mach-O file containing this symbol. pub fn macho_file(&self) -> &'file MachOFile<'data, Mach, R> { self.file } /// Get the raw Mach-O symbol structure. pub fn macho_symbol(&self) -> &'data Mach::Nlist { self.nlist } } impl<'data, 'file, Mach, R> read::private::Sealed for MachOSymbol<'data, 'file, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { } impl<'data, 'file, Mach, R> ObjectSymbol<'data> for MachOSymbol<'data, 'file, Mach, R> where Mach: MachHeader, R: ReadRef<'data>, { #[inline] fn index(&self) -> SymbolIndex { self.index } fn name_bytes(&self) -> Result<&'data [u8]> { self.nlist.name(self.file.endian, self.file.symbols.strings) } fn name(&self) -> Result<&'data str> { let name = self.name_bytes()?; str::from_utf8(name) .ok() .read_error("Non UTF-8 Mach-O symbol name") } #[inline] fn address(&self) -> u64 { self.nlist.n_value(self.file.endian).into() } #[inline] fn size(&self) -> u64 { 0 } fn kind(&self) -> SymbolKind { self.section() .index() .and_then(|index| self.file.section_internal(index).ok()) .map(|section| match section.kind { SectionKind::Text => SymbolKind::Text, SectionKind::Data | SectionKind::ReadOnlyData | SectionKind::ReadOnlyString | SectionKind::UninitializedData | SectionKind::Common => SymbolKind::Data, SectionKind::Tls | SectionKind::UninitializedTls | SectionKind::TlsVariables => { SymbolKind::Tls } _ => SymbolKind::Unknown, }) .unwrap_or(SymbolKind::Unknown) } fn section(&self) -> SymbolSection { match self.nlist.n_type() & macho::N_TYPE { macho::N_UNDF => SymbolSection::Undefined, macho::N_ABS => SymbolSection::Absolute, macho::N_SECT => { let n_sect = self.nlist.n_sect(); if n_sect != 0 { SymbolSection::Section(SectionIndex(n_sect as usize)) } else { SymbolSection::Unknown } } _ => SymbolSection::Unknown, } } #[inline] fn is_undefined(&self) -> bool { self.nlist.n_type() & macho::N_TYPE == macho::N_UNDF } #[inline] fn is_definition(&self) -> bool { self.nlist.is_definition() } #[inline] fn is_common(&self) -> bool { // Mach-O common symbols are based on section, not symbol false } #[inline] fn is_weak(&self) -> bool { self.nlist.n_desc(self.file.endian) & (macho::N_WEAK_REF | macho::N_WEAK_DEF) != 0 } fn scope(&self) -> SymbolScope { let n_type = self.nlist.n_type(); if n_type & macho::N_TYPE == macho::N_UNDF { SymbolScope::Unknown } else if n_type & macho::N_EXT == 0 { SymbolScope::Compilation } else if n_type & macho::N_PEXT != 0 { SymbolScope::Linkage } else { SymbolScope::Dynamic } } #[inline] fn is_global(&self) -> bool { self.scope() != SymbolScope::Compilation } #[inline] fn is_local(&self) -> bool { self.scope() == SymbolScope::Compilation } #[inline] fn flags(&self) -> SymbolFlags { let n_desc = self.nlist.n_desc(self.file.endian); SymbolFlags::MachO { n_desc } } } /// A trait for generic access to [`macho::Nlist32`] and [`macho::Nlist64`]. #[allow(missing_docs)] pub trait Nlist: Debug + Pod { type Word: Into; type Endian: endian::Endian; fn n_strx(&self, endian: Self::Endian) -> u32; fn n_type(&self) -> u8; fn n_sect(&self) -> u8; fn n_desc(&self, endian: Self::Endian) -> u16; fn n_value(&self, endian: Self::Endian) -> Self::Word; fn name<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, strings: StringTable<'data, R>, ) -> Result<&'data [u8]> { strings .get(self.n_strx(endian)) .read_error("Invalid Mach-O symbol name offset") } /// Return true if this is a STAB symbol. /// /// This determines the meaning of the `n_type` field. fn is_stab(&self) -> bool { self.n_type() & macho::N_STAB != 0 } /// Return true if this is an undefined symbol. fn is_undefined(&self) -> bool { let n_type = self.n_type(); n_type & macho::N_STAB == 0 && n_type & macho::N_TYPE == macho::N_UNDF } /// Return true if the symbol is a definition of a function or data object. fn is_definition(&self) -> bool { let n_type = self.n_type(); n_type & macho::N_STAB == 0 && n_type & macho::N_TYPE == macho::N_SECT } /// Return the library ordinal. /// /// This is either a 1-based index into the dylib load commands, /// or a special ordinal. #[inline] fn library_ordinal(&self, endian: Self::Endian) -> u8 { (self.n_desc(endian) >> 8) as u8 } } impl Nlist for macho::Nlist32 { type Word = u32; type Endian = Endian; fn n_strx(&self, endian: Self::Endian) -> u32 { self.n_strx.get(endian) } fn n_type(&self) -> u8 { self.n_type } fn n_sect(&self) -> u8 { self.n_sect } fn n_desc(&self, endian: Self::Endian) -> u16 { self.n_desc.get(endian) } fn n_value(&self, endian: Self::Endian) -> Self::Word { self.n_value.get(endian) } } impl Nlist for macho::Nlist64 { type Word = u64; type Endian = Endian; fn n_strx(&self, endian: Self::Endian) -> u32 { self.n_strx.get(endian) } fn n_type(&self) -> u8 { self.n_type } fn n_sect(&self) -> u8 { self.n_sect } fn n_desc(&self, endian: Self::Endian) -> u16 { self.n_desc.get(endian) } fn n_value(&self, endian: Self::Endian) -> Self::Word { self.n_value.get(endian) } } object-0.36.5/src/read/mod.rs000064400000000000000000000732541046102023000140270ustar 00000000000000//! Interface for reading object files. //! //! ## Unified read API //! //! The [`Object`] trait provides a unified read API for accessing common features of //! object files, such as sections and symbols. There is an implementation of this //! trait for [`File`], which allows reading any file format, as well as implementations //! for each file format: //! [`ElfFile`](elf::ElfFile), [`MachOFile`](macho::MachOFile), [`CoffFile`](coff::CoffFile), //! [`PeFile`](pe::PeFile), [`WasmFile`](wasm::WasmFile), [`XcoffFile`](xcoff::XcoffFile). //! //! ## Low level read API //! //! The submodules for each file format define helpers that operate on the raw structs. //! These can be used instead of the unified API, or in conjunction with it to access //! details that are not available via the unified API. //! //! See the [submodules](#modules) for examples of the low level read API. //! //! ## Naming Convention //! //! Types that form part of the unified API for a file format are prefixed with the //! name of the file format. //! //! ## Example for unified read API //! ```no_run //! use object::{Object, ObjectSection}; //! use std::error::Error; //! use std::fs; //! //! /// Reads a file and displays the name of each section. //! fn main() -> Result<(), Box> { //! # #[cfg(all(feature = "read", feature = "std"))] { //! let data = fs::read("path/to/binary")?; //! let file = object::File::parse(&*data)?; //! for section in file.sections() { //! println!("{}", section.name()?); //! } //! # } //! Ok(()) //! } //! ``` use alloc::borrow::Cow; use alloc::vec::Vec; use core::{fmt, result}; #[cfg(not(feature = "std"))] use alloc::collections::btree_map::BTreeMap as Map; #[cfg(feature = "std")] use std::collections::hash_map::HashMap as Map; pub use crate::common::*; mod read_ref; pub use read_ref::*; mod read_cache; pub use read_cache::*; mod util; pub use util::*; #[cfg(any(feature = "elf", feature = "macho"))] mod gnu_compression; #[cfg(any( feature = "coff", feature = "elf", feature = "macho", feature = "pe", feature = "wasm", feature = "xcoff" ))] mod any; #[cfg(any( feature = "coff", feature = "elf", feature = "macho", feature = "pe", feature = "wasm", feature = "xcoff" ))] pub use any::*; #[cfg(feature = "archive")] pub mod archive; #[cfg(feature = "coff")] pub mod coff; #[cfg(feature = "elf")] pub mod elf; #[cfg(feature = "macho")] pub mod macho; #[cfg(feature = "pe")] pub mod pe; #[cfg(feature = "wasm")] pub mod wasm; #[cfg(feature = "xcoff")] pub mod xcoff; mod traits; pub use traits::*; mod private { pub trait Sealed {} } /// The error type used within the read module. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Error(pub(crate) &'static str); impl fmt::Display for Error { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.0) } } #[cfg(feature = "std")] impl std::error::Error for Error {} /// The result type used within the read module. pub type Result = result::Result; trait ReadError { fn read_error(self, error: &'static str) -> Result; } impl ReadError for result::Result { fn read_error(self, error: &'static str) -> Result { self.map_err(|()| Error(error)) } } impl ReadError for result::Result { fn read_error(self, error: &'static str) -> Result { self.map_err(|_| Error(error)) } } impl ReadError for Option { fn read_error(self, error: &'static str) -> Result { self.ok_or(Error(error)) } } /// The native executable file for the target platform. #[cfg(all( unix, not(target_os = "macos"), target_pointer_width = "32", feature = "elf" ))] pub type NativeFile<'data, R = &'data [u8]> = elf::ElfFile32<'data, crate::endian::Endianness, R>; /// The native executable file for the target platform. #[cfg(all( unix, not(target_os = "macos"), target_pointer_width = "64", feature = "elf" ))] pub type NativeFile<'data, R = &'data [u8]> = elf::ElfFile64<'data, crate::endian::Endianness, R>; /// The native executable file for the target platform. #[cfg(all(target_os = "macos", target_pointer_width = "32", feature = "macho"))] pub type NativeFile<'data, R = &'data [u8]> = macho::MachOFile32<'data, crate::endian::Endianness, R>; /// The native executable file for the target platform. #[cfg(all(target_os = "macos", target_pointer_width = "64", feature = "macho"))] pub type NativeFile<'data, R = &'data [u8]> = macho::MachOFile64<'data, crate::endian::Endianness, R>; /// The native executable file for the target platform. #[cfg(all(target_os = "windows", target_pointer_width = "32", feature = "pe"))] pub type NativeFile<'data, R = &'data [u8]> = pe::PeFile32<'data, R>; /// The native executable file for the target platform. #[cfg(all(target_os = "windows", target_pointer_width = "64", feature = "pe"))] pub type NativeFile<'data, R = &'data [u8]> = pe::PeFile64<'data, R>; /// The native executable file for the target platform. #[cfg(all(feature = "wasm", target_arch = "wasm32", feature = "wasm"))] pub type NativeFile<'data, R = &'data [u8]> = wasm::WasmFile<'data, R>; /// A file format kind. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum FileKind { /// A Unix archive. /// /// See [`archive::ArchiveFile`]. #[cfg(feature = "archive")] Archive, /// A COFF object file. /// /// See [`coff::CoffFile`]. #[cfg(feature = "coff")] Coff, /// A COFF bigobj object file. /// /// This supports a larger number of sections. /// /// See [`coff::CoffBigFile`]. #[cfg(feature = "coff")] CoffBig, /// A Windows short import file. /// /// See [`coff::ImportFile`]. #[cfg(feature = "coff")] CoffImport, /// A dyld cache file containing Mach-O images. /// /// See [`macho::DyldCache`] #[cfg(feature = "macho")] DyldCache, /// A 32-bit ELF file. /// /// See [`elf::ElfFile32`]. #[cfg(feature = "elf")] Elf32, /// A 64-bit ELF file. /// /// See [`elf::ElfFile64`]. #[cfg(feature = "elf")] Elf64, /// A 32-bit Mach-O file. /// /// See [`macho::MachOFile32`]. #[cfg(feature = "macho")] MachO32, /// A 64-bit Mach-O file. /// /// See [`macho::MachOFile64`]. #[cfg(feature = "macho")] MachO64, /// A 32-bit Mach-O fat binary. /// /// See [`macho::MachOFatFile32`]. #[cfg(feature = "macho")] MachOFat32, /// A 64-bit Mach-O fat binary. /// /// See [`macho::MachOFatFile64`]. #[cfg(feature = "macho")] MachOFat64, /// A 32-bit PE file. /// /// See [`pe::PeFile32`]. #[cfg(feature = "pe")] Pe32, /// A 64-bit PE file. /// /// See [`pe::PeFile64`]. #[cfg(feature = "pe")] Pe64, /// A Wasm file. /// /// See [`wasm::WasmFile`]. #[cfg(feature = "wasm")] Wasm, /// A 32-bit XCOFF file. /// /// See [`xcoff::XcoffFile32`]. #[cfg(feature = "xcoff")] Xcoff32, /// A 64-bit XCOFF file. /// /// See [`xcoff::XcoffFile64`]. #[cfg(feature = "xcoff")] Xcoff64, } impl FileKind { /// Determine a file kind by parsing the start of the file. pub fn parse<'data, R: ReadRef<'data>>(data: R) -> Result { Self::parse_at(data, 0) } /// Determine a file kind by parsing at the given offset. pub fn parse_at<'data, R: ReadRef<'data>>(data: R, offset: u64) -> Result { let magic = data .read_bytes_at(offset, 16) .read_error("Could not read file magic")?; if magic.len() < 16 { return Err(Error("File too short")); } let kind = match [magic[0], magic[1], magic[2], magic[3], magic[4], magic[5], magic[6], magic[7]] { #[cfg(feature = "archive")] [b'!', b'<', b'a', b'r', b'c', b'h', b'>', b'\n'] | [b'!', b'<', b't', b'h', b'i', b'n', b'>', b'\n'] => FileKind::Archive, #[cfg(feature = "macho")] [b'd', b'y', b'l', b'd', b'_', b'v', b'1', b' '] => FileKind::DyldCache, #[cfg(feature = "elf")] [0x7f, b'E', b'L', b'F', 1, ..] => FileKind::Elf32, #[cfg(feature = "elf")] [0x7f, b'E', b'L', b'F', 2, ..] => FileKind::Elf64, #[cfg(feature = "macho")] [0xfe, 0xed, 0xfa, 0xce, ..] | [0xce, 0xfa, 0xed, 0xfe, ..] => FileKind::MachO32, #[cfg(feature = "macho")] | [0xfe, 0xed, 0xfa, 0xcf, ..] | [0xcf, 0xfa, 0xed, 0xfe, ..] => FileKind::MachO64, #[cfg(feature = "macho")] [0xca, 0xfe, 0xba, 0xbe, ..] => FileKind::MachOFat32, #[cfg(feature = "macho")] [0xca, 0xfe, 0xba, 0xbf, ..] => FileKind::MachOFat64, #[cfg(feature = "wasm")] [0x00, b'a', b's', b'm', _, _, 0x00, 0x00] => FileKind::Wasm, #[cfg(feature = "pe")] [b'M', b'Z', ..] if offset == 0 => { // offset == 0 restriction is because optional_header_magic only looks at offset 0 match pe::optional_header_magic(data) { Ok(crate::pe::IMAGE_NT_OPTIONAL_HDR32_MAGIC) => { FileKind::Pe32 } Ok(crate::pe::IMAGE_NT_OPTIONAL_HDR64_MAGIC) => { FileKind::Pe64 } _ => return Err(Error("Unknown MS-DOS file")), } } // TODO: more COFF machines #[cfg(feature = "coff")] // COFF arm [0xc4, 0x01, ..] // COFF arm64 | [0x64, 0xaa, ..] // COFF arm64ec | [0x41, 0xa6, ..] // COFF x86 | [0x4c, 0x01, ..] // COFF x86-64 | [0x64, 0x86, ..] => FileKind::Coff, #[cfg(feature = "coff")] [0x00, 0x00, 0xff, 0xff, 0x00, 0x00, ..] => FileKind::CoffImport, #[cfg(feature = "coff")] [0x00, 0x00, 0xff, 0xff, 0x02, 0x00, ..] if offset == 0 => { // offset == 0 restriction is because anon_object_class_id only looks at offset 0 match coff::anon_object_class_id(data) { Ok(crate::pe::ANON_OBJECT_HEADER_BIGOBJ_CLASS_ID) => FileKind::CoffBig, _ => return Err(Error("Unknown anon object file")), } } #[cfg(feature = "xcoff")] [0x01, 0xdf, ..] => FileKind::Xcoff32, #[cfg(feature = "xcoff")] [0x01, 0xf7, ..] => FileKind::Xcoff64, _ => return Err(Error("Unknown file magic")), }; Ok(kind) } } /// An object kind. /// /// Returned by [`Object::kind`]. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum ObjectKind { /// The object kind is unknown. Unknown, /// Relocatable object. Relocatable, /// Executable. Executable, /// Dynamic shared object. Dynamic, /// Core. Core, } /// The index used to identify a section in a file. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct SectionIndex(pub usize); impl fmt::Display for SectionIndex { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } /// The index used to identify a symbol in a symbol table. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct SymbolIndex(pub usize); impl fmt::Display for SymbolIndex { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } /// The section where an [`ObjectSymbol`] is defined. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum SymbolSection { /// The section is unknown. Unknown, /// The section is not applicable for this symbol (such as file symbols). None, /// The symbol is undefined. Undefined, /// The symbol has an absolute value. Absolute, /// The symbol is a zero-initialized symbol that will be combined with duplicate definitions. Common, /// The symbol is defined in the given section. Section(SectionIndex), } impl SymbolSection { /// Returns the section index for the section where the symbol is defined. /// /// May return `None` if the symbol is not defined in a section. #[inline] pub fn index(self) -> Option { if let SymbolSection::Section(index) = self { Some(index) } else { None } } } /// An entry in a [`SymbolMap`]. pub trait SymbolMapEntry { /// The symbol address. fn address(&self) -> u64; } /// A map from addresses to symbol information. /// /// The symbol information depends on the chosen entry type, such as [`SymbolMapName`]. /// /// Returned by [`Object::symbol_map`]. #[derive(Debug, Default, Clone)] pub struct SymbolMap { symbols: Vec, } impl SymbolMap { /// Construct a new symbol map. /// /// This function will sort the symbols by address. pub fn new(mut symbols: Vec) -> Self { symbols.sort_by_key(|s| s.address()); SymbolMap { symbols } } /// Get the symbol before the given address. pub fn get(&self, address: u64) -> Option<&T> { let index = match self .symbols .binary_search_by_key(&address, |symbol| symbol.address()) { Ok(index) => index, Err(index) => index.checked_sub(1)?, }; self.symbols.get(index) } /// Get all symbols in the map. #[inline] pub fn symbols(&self) -> &[T] { &self.symbols } } /// The type used for entries in a [`SymbolMap`] that maps from addresses to names. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct SymbolMapName<'data> { address: u64, name: &'data str, } impl<'data> SymbolMapName<'data> { /// Construct a `SymbolMapName`. pub fn new(address: u64, name: &'data str) -> Self { SymbolMapName { address, name } } /// The symbol address. #[inline] pub fn address(&self) -> u64 { self.address } /// The symbol name. #[inline] pub fn name(&self) -> &'data str { self.name } } impl<'data> SymbolMapEntry for SymbolMapName<'data> { #[inline] fn address(&self) -> u64 { self.address } } /// A map from addresses to symbol names and object files. /// /// This is derived from STAB entries in Mach-O files. /// /// Returned by [`Object::object_map`]. #[derive(Debug, Default, Clone)] pub struct ObjectMap<'data> { symbols: SymbolMap>, objects: Vec>, } impl<'data> ObjectMap<'data> { /// Get the entry containing the given address. pub fn get(&self, address: u64) -> Option<&ObjectMapEntry<'data>> { self.symbols .get(address) .filter(|entry| entry.size == 0 || address.wrapping_sub(entry.address) < entry.size) } /// Get all symbols in the map. #[inline] pub fn symbols(&self) -> &[ObjectMapEntry<'data>] { self.symbols.symbols() } /// Get all objects in the map. #[inline] pub fn objects(&self) -> &[ObjectMapFile<'data>] { &self.objects } } /// A symbol in an [`ObjectMap`]. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] pub struct ObjectMapEntry<'data> { address: u64, size: u64, name: &'data [u8], object: usize, } impl<'data> ObjectMapEntry<'data> { /// Get the symbol address. #[inline] pub fn address(&self) -> u64 { self.address } /// Get the symbol size. /// /// This may be 0 if the size is unknown. #[inline] pub fn size(&self) -> u64 { self.size } /// Get the symbol name. #[inline] pub fn name(&self) -> &'data [u8] { self.name } /// Get the index of the object file name. #[inline] pub fn object_index(&self) -> usize { self.object } /// Get the object file name. #[inline] pub fn object<'a>(&self, map: &'a ObjectMap<'data>) -> &'a ObjectMapFile<'data> { &map.objects[self.object] } } impl<'data> SymbolMapEntry for ObjectMapEntry<'data> { #[inline] fn address(&self) -> u64 { self.address } } /// An object file name in an [`ObjectMap`]. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ObjectMapFile<'data> { path: &'data [u8], member: Option<&'data [u8]>, } impl<'data> ObjectMapFile<'data> { #[cfg(feature = "macho")] fn new(path: &'data [u8], member: Option<&'data [u8]>) -> Self { ObjectMapFile { path, member } } /// Get the path to the file containing the object. #[inline] pub fn path(&self) -> &'data [u8] { self.path } /// If the file is an archive, get the name of the member containing the object. #[inline] pub fn member(&self) -> Option<&'data [u8]> { self.member } } /// An imported symbol. /// /// Returned by [`Object::imports`]. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Import<'data> { library: ByteString<'data>, // TODO: or ordinal name: ByteString<'data>, } impl<'data> Import<'data> { /// The symbol name. #[inline] pub fn name(&self) -> &'data [u8] { self.name.0 } /// The name of the library to import the symbol from. #[inline] pub fn library(&self) -> &'data [u8] { self.library.0 } } /// An exported symbol. /// /// Returned by [`Object::exports`]. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Export<'data> { // TODO: and ordinal? name: ByteString<'data>, address: u64, } impl<'data> Export<'data> { /// The symbol name. #[inline] pub fn name(&self) -> &'data [u8] { self.name.0 } /// The virtual address of the symbol. #[inline] pub fn address(&self) -> u64 { self.address } } /// PDB information from the debug directory in a PE file. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct CodeView<'data> { guid: [u8; 16], path: ByteString<'data>, age: u32, } impl<'data> CodeView<'data> { /// The path to the PDB as stored in CodeView. #[inline] pub fn path(&self) -> &'data [u8] { self.path.0 } /// The age of the PDB. #[inline] pub fn age(&self) -> u32 { self.age } /// The GUID of the PDB. #[inline] pub fn guid(&self) -> [u8; 16] { self.guid } } /// The target referenced by a [`Relocation`]. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum RelocationTarget { /// The target is a symbol. Symbol(SymbolIndex), /// The target is a section. Section(SectionIndex), /// The offset is an absolute address. Absolute, } /// A relocation entry. /// /// Returned by [`Object::dynamic_relocations`] or [`ObjectSection::relocations`]. #[derive(Debug)] pub struct Relocation { kind: RelocationKind, encoding: RelocationEncoding, size: u8, target: RelocationTarget, addend: i64, implicit_addend: bool, flags: RelocationFlags, } impl Relocation { /// The operation used to calculate the result of the relocation. #[inline] pub fn kind(&self) -> RelocationKind { self.kind } /// Information about how the result of the relocation operation is encoded in the place. #[inline] pub fn encoding(&self) -> RelocationEncoding { self.encoding } /// The size in bits of the place of the relocation. /// /// If 0, then the size is determined by the relocation kind. #[inline] pub fn size(&self) -> u8 { self.size } /// The target of the relocation. #[inline] pub fn target(&self) -> RelocationTarget { self.target } /// The addend to use in the relocation calculation. #[inline] pub fn addend(&self) -> i64 { self.addend } /// Set the addend to use in the relocation calculation. #[inline] pub fn set_addend(&mut self, addend: i64) { self.addend = addend; } /// Returns true if there is an implicit addend stored in the data at the offset /// to be relocated. #[inline] pub fn has_implicit_addend(&self) -> bool { self.implicit_addend } /// Relocation flags that are specific to each file format. /// /// The values returned by `kind`, `encoding` and `size` are derived /// from these flags. #[inline] pub fn flags(&self) -> RelocationFlags { self.flags } } /// A map from section offsets to relocation information. /// /// This can be used to apply relocations to a value at a given section offset. /// This is intended for use with DWARF in relocatable object files, and only /// supports relocations that are used in DWARF. /// /// Returned by [`ObjectSection::relocation_map`]. #[derive(Debug, Default)] pub struct RelocationMap(Map); impl RelocationMap { /// Construct a new relocation map for a section. /// /// Fails if any relocation cannot be added to the map. /// You can manually use `add` if you need different error handling, /// such as to list all errors or to ignore them. pub fn new<'data, 'file, T>(file: &'file T, section: &T::Section<'file>) -> Result where T: Object<'data>, { let mut map = RelocationMap(Map::new()); for (offset, relocation) in section.relocations() { map.add(file, offset, relocation)?; } Ok(map) } /// Add a single relocation to the map. pub fn add<'data: 'file, 'file, T>( &mut self, file: &'file T, offset: u64, relocation: Relocation, ) -> Result<()> where T: Object<'data>, { let mut entry = RelocationMapEntry { implicit_addend: relocation.has_implicit_addend(), addend: relocation.addend() as u64, }; match relocation.kind() { RelocationKind::Absolute => match relocation.target() { RelocationTarget::Symbol(symbol_idx) => { let symbol = file .symbol_by_index(symbol_idx) .read_error("Relocation with invalid symbol")?; entry.addend = symbol.address().wrapping_add(entry.addend); } RelocationTarget::Section(section_idx) => { let section = file .section_by_index(section_idx) .read_error("Relocation with invalid section")?; // DWARF parsers expect references to DWARF sections to be section offsets, // not addresses. Addresses are useful for everything else. if section.kind() != SectionKind::Debug { entry.addend = section.address().wrapping_add(entry.addend); } } _ => { return Err(Error("Unsupported relocation target")); } }, _ => { return Err(Error("Unsupported relocation type")); } } if self.0.insert(offset, entry).is_some() { return Err(Error("Multiple relocations for offset")); } Ok(()) } /// Relocate a value that was read from the section at the given offset. pub fn relocate(&self, offset: u64, value: u64) -> u64 { if let Some(relocation) = self.0.get(&offset) { if relocation.implicit_addend { // Use the explicit addend too, because it may have the symbol value. value.wrapping_add(relocation.addend) } else { relocation.addend } } else { value } } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] struct RelocationMapEntry { implicit_addend: bool, addend: u64, } /// A data compression format. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum CompressionFormat { /// The data is uncompressed. None, /// The data is compressed, but the compression format is unknown. Unknown, /// ZLIB/DEFLATE. /// /// Used for ELF compression and GNU compressed debug information. Zlib, /// Zstandard. /// /// Used for ELF compression. Zstandard, } /// A range in a file that may be compressed. /// /// Returned by [`ObjectSection::compressed_file_range`]. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct CompressedFileRange { /// The data compression format. pub format: CompressionFormat, /// The file offset of the compressed data. pub offset: u64, /// The compressed data size. pub compressed_size: u64, /// The uncompressed data size. pub uncompressed_size: u64, } impl CompressedFileRange { /// Data that is uncompressed. #[inline] pub fn none(range: Option<(u64, u64)>) -> Self { if let Some((offset, size)) = range { CompressedFileRange { format: CompressionFormat::None, offset, compressed_size: size, uncompressed_size: size, } } else { CompressedFileRange { format: CompressionFormat::None, offset: 0, compressed_size: 0, uncompressed_size: 0, } } } /// Convert to [`CompressedData`] by reading from the file. pub fn data<'data, R: ReadRef<'data>>(self, file: R) -> Result> { let data = file .read_bytes_at(self.offset, self.compressed_size) .read_error("Invalid compressed data size or offset")?; Ok(CompressedData { format: self.format, data, uncompressed_size: self.uncompressed_size, }) } } /// Data that may be compressed. /// /// Returned by [`ObjectSection::compressed_data`]. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct CompressedData<'data> { /// The data compression format. pub format: CompressionFormat, /// The compressed data. pub data: &'data [u8], /// The uncompressed data size. pub uncompressed_size: u64, } impl<'data> CompressedData<'data> { /// Data that is uncompressed. #[inline] pub fn none(data: &'data [u8]) -> Self { CompressedData { format: CompressionFormat::None, data, uncompressed_size: data.len() as u64, } } /// Return the uncompressed data. /// /// Returns an error for invalid data or unsupported compression. /// This includes if the data is compressed but the `compression` feature /// for this crate is disabled. pub fn decompress(self) -> Result> { match self.format { CompressionFormat::None => Ok(Cow::Borrowed(self.data)), #[cfg(feature = "compression")] CompressionFormat::Zlib | CompressionFormat::Zstandard => { use core::convert::TryInto; use std::io::Read; let size = self .uncompressed_size .try_into() .ok() .read_error("Uncompressed data size is too large.")?; let mut decompressed = Vec::new(); decompressed .try_reserve_exact(size) .ok() .read_error("Uncompressed data allocation failed")?; match self.format { CompressionFormat::Zlib => { let mut decompress = flate2::Decompress::new(true); decompress .decompress_vec( self.data, &mut decompressed, flate2::FlushDecompress::Finish, ) .ok() .read_error("Invalid zlib compressed data")?; } CompressionFormat::Zstandard => { let mut input = self.data; while !input.is_empty() { let mut decoder = match ruzstd::StreamingDecoder::new(&mut input) { Ok(decoder) => decoder, Err( ruzstd::frame_decoder::FrameDecoderError::ReadFrameHeaderError( ruzstd::frame::ReadFrameHeaderError::SkipFrame { length, .. }, ), ) => { input = input .get(length as usize..) .read_error("Invalid zstd compressed data")?; continue; } x => x.ok().read_error("Invalid zstd compressed data")?, }; decoder .read_to_end(&mut decompressed) .ok() .read_error("Invalid zstd compressed data")?; } } _ => unreachable!(), } if size != decompressed.len() { return Err(Error( "Uncompressed data size does not match compression header", )); } Ok(Cow::Owned(decompressed)) } _ => Err(Error("Unsupported compressed data.")), } } } object-0.36.5/src/read/pe/data_directory.rs000064400000000000000000000173531046102023000166470ustar 00000000000000use core::slice; use crate::endian::LittleEndian as LE; use crate::pe; use crate::read::{Error, ReadError, ReadRef, Result}; use super::{ DelayLoadImportTable, ExportTable, ImportTable, RelocationBlockIterator, ResourceDirectory, SectionTable, }; /// The table of data directories in a PE file. /// /// Returned by [`ImageNtHeaders::parse`](super::ImageNtHeaders::parse). #[derive(Debug, Clone, Copy)] pub struct DataDirectories<'data> { entries: &'data [pe::ImageDataDirectory], } impl<'data> DataDirectories<'data> { /// Parse the data directory table. /// /// `data` must be the remaining optional data following the /// [optional header](pe::ImageOptionalHeader64). `number` must be from the /// [`number_of_rva_and_sizes`](pe::ImageOptionalHeader64::number_of_rva_and_sizes) /// field of the optional header. pub fn parse(data: &'data [u8], number: u32) -> Result { let entries = data .read_slice_at(0, number as usize) .read_error("Invalid PE number of RVA and sizes")?; Ok(DataDirectories { entries }) } /// The number of data directories. #[allow(clippy::len_without_is_empty)] pub fn len(&self) -> usize { self.entries.len() } /// Iterator over the data directories. pub fn iter(&self) -> slice::Iter<'data, pe::ImageDataDirectory> { self.entries.iter() } /// Iterator which gives the directories as well as their index (one of the IMAGE_DIRECTORY_ENTRY_* constants). pub fn enumerate(&self) -> core::iter::Enumerate> { self.entries.iter().enumerate() } /// Returns the data directory at the given index. /// /// Index should be one of the `IMAGE_DIRECTORY_ENTRY_*` constants. /// /// Returns `None` if the index is larger than the table size, /// or if the entry at the index has a zero virtual address. pub fn get(&self, index: usize) -> Option<&'data pe::ImageDataDirectory> { self.entries .get(index) .filter(|d| d.virtual_address.get(LE) != 0) } /// Returns the unparsed export directory. /// /// `data` must be the entire file data. pub fn export_directory>( &self, data: R, sections: &SectionTable<'data>, ) -> Result> { let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_EXPORT) { Some(data_dir) => data_dir, None => return Ok(None), }; let export_data = data_dir.data(data, sections)?; ExportTable::parse_directory(export_data).map(Some) } /// Returns the partially parsed export directory. /// /// `data` must be the entire file data. pub fn export_table>( &self, data: R, sections: &SectionTable<'data>, ) -> Result>> { let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_EXPORT) { Some(data_dir) => data_dir, None => return Ok(None), }; let export_va = data_dir.virtual_address.get(LE); let export_data = data_dir.data(data, sections)?; ExportTable::parse(export_data, export_va).map(Some) } /// Returns the partially parsed import directory. /// /// `data` must be the entire file data. pub fn import_table>( &self, data: R, sections: &SectionTable<'data>, ) -> Result>> { let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_IMPORT) { Some(data_dir) => data_dir, None => return Ok(None), }; let import_va = data_dir.virtual_address.get(LE); let (section_data, section_va) = sections .pe_data_containing(data, import_va) .read_error("Invalid import data dir virtual address")?; Ok(Some(ImportTable::new(section_data, section_va, import_va))) } /// Returns the partially parsed delay-load import directory. /// /// `data` must be the entire file data. pub fn delay_load_import_table>( &self, data: R, sections: &SectionTable<'data>, ) -> Result>> { let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT) { Some(data_dir) => data_dir, None => return Ok(None), }; let import_va = data_dir.virtual_address.get(LE); let (section_data, section_va) = sections .pe_data_containing(data, import_va) .read_error("Invalid import data dir virtual address")?; Ok(Some(DelayLoadImportTable::new( section_data, section_va, import_va, ))) } /// Returns the blocks in the base relocation directory. /// /// `data` must be the entire file data. pub fn relocation_blocks>( &self, data: R, sections: &SectionTable<'data>, ) -> Result>> { let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_BASERELOC) { Some(data_dir) => data_dir, None => return Ok(None), }; let reloc_data = data_dir.data(data, sections)?; Ok(Some(RelocationBlockIterator::new(reloc_data))) } /// Returns the resource directory. /// /// `data` must be the entire file data. pub fn resource_directory>( &self, data: R, sections: &SectionTable<'data>, ) -> Result>> { let data_dir = match self.get(pe::IMAGE_DIRECTORY_ENTRY_RESOURCE) { Some(data_dir) => data_dir, None => return Ok(None), }; let rsrc_data = data_dir.data(data, sections)?; Ok(Some(ResourceDirectory::new(rsrc_data))) } } impl pe::ImageDataDirectory { /// Return the virtual address range of this directory entry. pub fn address_range(&self) -> (u32, u32) { (self.virtual_address.get(LE), self.size.get(LE)) } /// Return the file offset and size of this directory entry. /// /// This function has some limitations: /// - It requires that the data is contained in a single section. /// - It uses the size field of the directory entry, which is /// not desirable for all data directories. /// - It uses the `virtual_address` of the directory entry as an address, /// which is not valid for `IMAGE_DIRECTORY_ENTRY_SECURITY`. pub fn file_range(&self, sections: &SectionTable<'_>) -> Result<(u32, u32)> { let (offset, section_size) = sections .pe_file_range_at(self.virtual_address.get(LE)) .read_error("Invalid data dir virtual address")?; let size = self.size.get(LE); if size > section_size { return Err(Error("Invalid data dir size")); } Ok((offset, size)) } /// Get the data referenced by this directory entry. /// /// This function has some limitations: /// - It requires that the data is contained in a single section. /// - It uses the size field of the directory entry, which is /// not desirable for all data directories. /// - It uses the `virtual_address` of the directory entry as an address, /// which is not valid for `IMAGE_DIRECTORY_ENTRY_SECURITY`. pub fn data<'data, R: ReadRef<'data>>( &self, data: R, sections: &SectionTable<'data>, ) -> Result<&'data [u8]> { sections .pe_data_at(data, self.virtual_address.get(LE)) .read_error("Invalid data dir virtual address")? .get(..self.size.get(LE) as usize) .read_error("Invalid data dir size") } } object-0.36.5/src/read/pe/export.rs000064400000000000000000000271611046102023000151710ustar 00000000000000use alloc::vec::Vec; use core::fmt::Debug; use crate::endian::{LittleEndian as LE, U16Bytes, U32Bytes}; use crate::pe; use crate::read::{ByteString, Bytes, Error, ReadError, ReadRef, Result}; /// Where an export is pointing to. #[derive(Clone, Copy)] pub enum ExportTarget<'data> { /// The address of the export, relative to the image base. Address(u32), /// Forwarded to an export ordinal in another DLL. /// /// This gives the name of the DLL, and the ordinal. ForwardByOrdinal(&'data [u8], u32), /// Forwarded to an export name in another DLL. /// /// This gives the name of the DLL, and the export name. ForwardByName(&'data [u8], &'data [u8]), } impl<'data> ExportTarget<'data> { /// Returns true if the target is an address. pub fn is_address(&self) -> bool { match self { ExportTarget::Address(_) => true, _ => false, } } /// Returns true if the export is forwarded to another DLL. pub fn is_forward(&self) -> bool { !self.is_address() } } /// An export from a PE file. /// /// There are multiple kinds of PE exports (with or without a name, and local or forwarded). #[derive(Clone, Copy)] pub struct Export<'data> { /// The ordinal of the export. /// /// These are sequential, starting at a base specified in the DLL. pub ordinal: u32, /// The name of the export, if known. pub name: Option<&'data [u8]>, /// The target of this export. pub target: ExportTarget<'data>, } impl<'a> Debug for Export<'a> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::result::Result<(), core::fmt::Error> { f.debug_struct("Export") .field("ordinal", &self.ordinal) .field("name", &self.name.map(ByteString)) .field("target", &self.target) .finish() } } impl<'a> Debug for ExportTarget<'a> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::result::Result<(), core::fmt::Error> { match self { ExportTarget::Address(address) => write!(f, "Address({:#x})", address), ExportTarget::ForwardByOrdinal(library, ordinal) => write!( f, "ForwardByOrdinal({:?}.#{})", ByteString(library), ordinal ), ExportTarget::ForwardByName(library, name) => write!( f, "ForwardByName({:?}.{:?})", ByteString(library), ByteString(name) ), } } } /// A partially parsed PE export table. /// /// Returned by [`DataDirectories::export_table`](super::DataDirectories::export_table). #[derive(Debug, Clone)] pub struct ExportTable<'data> { data: Bytes<'data>, virtual_address: u32, directory: &'data pe::ImageExportDirectory, addresses: &'data [U32Bytes], names: &'data [U32Bytes], name_ordinals: &'data [U16Bytes], } impl<'data> ExportTable<'data> { /// Parse the export table given its section data and address. pub fn parse(data: &'data [u8], virtual_address: u32) -> Result { let directory = Self::parse_directory(data)?; let data = Bytes(data); let mut addresses = &[][..]; let address_of_functions = directory.address_of_functions.get(LE); if address_of_functions != 0 { addresses = data .read_slice_at::>( address_of_functions.wrapping_sub(virtual_address) as usize, directory.number_of_functions.get(LE) as usize, ) .read_error("Invalid PE export address table")?; } let mut names = &[][..]; let mut name_ordinals = &[][..]; let address_of_names = directory.address_of_names.get(LE); let address_of_name_ordinals = directory.address_of_name_ordinals.get(LE); if address_of_names != 0 { if address_of_name_ordinals == 0 { return Err(Error("Missing PE export ordinal table")); } let number = directory.number_of_names.get(LE) as usize; names = data .read_slice_at::>( address_of_names.wrapping_sub(virtual_address) as usize, number, ) .read_error("Invalid PE export name pointer table")?; name_ordinals = data .read_slice_at::>( address_of_name_ordinals.wrapping_sub(virtual_address) as usize, number, ) .read_error("Invalid PE export ordinal table")?; } Ok(ExportTable { data, virtual_address, directory, addresses, names, name_ordinals, }) } /// Parse the export directory given its section data. pub fn parse_directory(data: &'data [u8]) -> Result<&'data pe::ImageExportDirectory> { data.read_at::(0) .read_error("Invalid PE export dir size") } /// Returns the header of the export table. pub fn directory(&self) -> &'data pe::ImageExportDirectory { self.directory } /// Returns the base value of ordinals. /// /// Adding this to an address index will give an ordinal. pub fn ordinal_base(&self) -> u32 { self.directory.base.get(LE) } /// Returns the unparsed address table. /// /// An address table entry may be a local address, or the address of a forwarded export entry. /// See [`Self::is_forward`] and [`Self::target_from_address`]. pub fn addresses(&self) -> &'data [U32Bytes] { self.addresses } /// Returns the unparsed name pointer table. /// /// A name pointer table entry can be used with [`Self::name_from_pointer`]. pub fn name_pointers(&self) -> &'data [U32Bytes] { self.names } /// Returns the unparsed ordinal table. /// /// An ordinal table entry is a 0-based index into the address table. /// See [`Self::address_by_index`] and [`Self::target_by_index`]. pub fn name_ordinals(&self) -> &'data [U16Bytes] { self.name_ordinals } /// Returns an iterator for the entries in the name pointer table and ordinal table. /// /// A name pointer table entry can be used with [`Self::name_from_pointer`]. /// /// An ordinal table entry is a 0-based index into the address table. /// See [`Self::address_by_index`] and [`Self::target_by_index`]. pub fn name_iter(&self) -> impl Iterator + 'data { self.names .iter() .map(|x| x.get(LE)) .zip(self.name_ordinals.iter().map(|x| x.get(LE))) } /// Returns the export address table entry at the given address index. /// /// This may be a local address, or the address of a forwarded export entry. /// See [`Self::is_forward`] and [`Self::target_from_address`]. /// /// `index` is a 0-based index into the export address table. pub fn address_by_index(&self, index: u32) -> Result { Ok(self .addresses .get(index as usize) .read_error("Invalid PE export address index")? .get(LE)) } /// Returns the export address table entry at the given ordinal. /// /// This may be a local address, or the address of a forwarded export entry. /// See [`Self::is_forward`] and [`Self::target_from_address`]. pub fn address_by_ordinal(&self, ordinal: u32) -> Result { self.address_by_index(ordinal.wrapping_sub(self.ordinal_base())) } /// Returns the target of the export at the given address index. /// /// `index` is a 0-based index into the export address table. pub fn target_by_index(&self, index: u32) -> Result> { self.target_from_address(self.address_by_index(index)?) } /// Returns the target of the export at the given ordinal. pub fn target_by_ordinal(&self, ordinal: u32) -> Result> { self.target_from_address(self.address_by_ordinal(ordinal)?) } /// Convert an export address table entry into a target. pub fn target_from_address(&self, address: u32) -> Result> { Ok(if let Some(forward) = self.forward_string(address)? { let i = forward .iter() .position(|x| *x == b'.') .read_error("Missing PE forwarded export separator")?; let library = &forward[..i]; match &forward[i + 1..] { [b'#', digits @ ..] => { let ordinal = parse_ordinal(digits).read_error("Invalid PE forwarded export ordinal")?; ExportTarget::ForwardByOrdinal(library, ordinal) } [] => { return Err(Error("Missing PE forwarded export name")); } name => ExportTarget::ForwardByName(library, name), } } else { ExportTarget::Address(address) }) } fn forward_offset(&self, address: u32) -> Option { let offset = address.wrapping_sub(self.virtual_address) as usize; if offset < self.data.len() { Some(offset) } else { None } } /// Return true if the export address table entry is a forward. pub fn is_forward(&self, address: u32) -> bool { self.forward_offset(address).is_some() } /// Return the forward string if the export address table entry is a forward. pub fn forward_string(&self, address: u32) -> Result> { if let Some(offset) = self.forward_offset(address) { self.data .read_string_at(offset) .read_error("Invalid PE forwarded export address") .map(Some) } else { Ok(None) } } /// Convert an export name pointer table entry into a name. pub fn name_from_pointer(&self, name_pointer: u32) -> Result<&'data [u8]> { let offset = name_pointer.wrapping_sub(self.virtual_address); self.data .read_string_at(offset as usize) .read_error("Invalid PE export name pointer") } /// Returns the parsed exports in this table. pub fn exports(&self) -> Result>> { // First, let's list all exports. let mut exports = Vec::new(); let ordinal_base = self.ordinal_base(); for (i, address) in self.addresses.iter().enumerate() { // Convert from an array index to an ordinal. let ordinal = ordinal_base.wrapping_add(i as u32); let target = self.target_from_address(address.get(LE))?; exports.push(Export { ordinal, target, // Might be populated later. name: None, }); } // Now, check whether some (or all) of them have an associated name. // `ordinal_index` is a 0-based index into `addresses`. for (name_pointer, ordinal_index) in self.name_iter() { let name = self.name_from_pointer(name_pointer)?; exports .get_mut(ordinal_index as usize) .read_error("Invalid PE export ordinal")? .name = Some(name); } Ok(exports) } } fn parse_ordinal(digits: &[u8]) -> Option { if digits.is_empty() { return None; } let mut result: u32 = 0; for &c in digits { let x = (c as char).to_digit(10)?; result = result.checked_mul(10)?.checked_add(x)?; } Some(result) } object-0.36.5/src/read/pe/file.rs000064400000000000000000000730301046102023000145630ustar 00000000000000use alloc::vec::Vec; use core::fmt::Debug; use core::{mem, str}; use core::convert::TryInto; use crate::endian::{LittleEndian as LE, U32}; use crate::pe; use crate::pod::{self, Pod}; use crate::read::coff::{CoffCommon, CoffSymbol, CoffSymbolIterator, CoffSymbolTable, SymbolTable}; use crate::read::{ self, Architecture, ByteString, Bytes, CodeView, ComdatKind, Error, Export, FileFlags, Import, NoDynamicRelocationIterator, Object, ObjectComdat, ObjectKind, ReadError, ReadRef, Result, SectionIndex, SubArchitecture, SymbolIndex, }; use super::{ DataDirectories, ExportTable, ImageThunkData, ImportTable, PeSection, PeSectionIterator, PeSegment, PeSegmentIterator, RichHeaderInfo, SectionTable, }; /// A PE32 (32-bit) image file. /// /// This is a file that starts with [`pe::ImageNtHeaders32`], and corresponds /// to [`crate::FileKind::Pe32`]. pub type PeFile32<'data, R = &'data [u8]> = PeFile<'data, pe::ImageNtHeaders32, R>; /// A PE32+ (64-bit) image file. /// /// This is a file that starts with [`pe::ImageNtHeaders64`], and corresponds /// to [`crate::FileKind::Pe64`]. pub type PeFile64<'data, R = &'data [u8]> = PeFile<'data, pe::ImageNtHeaders64, R>; /// A PE image file. /// /// Most functionality is provided by the [`Object`] trait implementation. #[derive(Debug)] pub struct PeFile<'data, Pe, R = &'data [u8]> where Pe: ImageNtHeaders, R: ReadRef<'data>, { pub(super) dos_header: &'data pe::ImageDosHeader, pub(super) nt_headers: &'data Pe, pub(super) data_directories: DataDirectories<'data>, pub(super) common: CoffCommon<'data, R>, pub(super) data: R, } impl<'data, Pe, R> PeFile<'data, Pe, R> where Pe: ImageNtHeaders, R: ReadRef<'data>, { /// Parse the raw PE file data. pub fn parse(data: R) -> Result { let dos_header = pe::ImageDosHeader::parse(data)?; let mut offset = dos_header.nt_headers_offset().into(); let (nt_headers, data_directories) = Pe::parse(data, &mut offset)?; let sections = nt_headers.sections(data, offset)?; let coff_symbols = nt_headers.symbols(data); let image_base = nt_headers.optional_header().image_base(); Ok(PeFile { dos_header, nt_headers, data_directories, common: CoffCommon { sections, // The PE file format deprecates the COFF symbol table (https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#coff-file-header-object-and-image) // We do not want to prevent parsing the rest of the PE file for a corrupt COFF header, but rather return an empty symbol table symbols: coff_symbols.unwrap_or_default(), image_base, }, data, }) } /// Returns this binary data. pub fn data(&self) -> R { self.data } /// Return the DOS header of this file. pub fn dos_header(&self) -> &'data pe::ImageDosHeader { self.dos_header } /// Return the NT Headers of this file. pub fn nt_headers(&self) -> &'data Pe { self.nt_headers } /// Returns information about the rich header of this file (if any). pub fn rich_header_info(&self) -> Option> { RichHeaderInfo::parse(self.data, self.dos_header.nt_headers_offset().into()) } /// Returns the section table of this binary. pub fn section_table(&self) -> SectionTable<'data> { self.common.sections } /// Returns the data directories of this file. pub fn data_directories(&self) -> DataDirectories<'data> { self.data_directories } /// Returns the data directory at the given index. pub fn data_directory(&self, id: usize) -> Option<&'data pe::ImageDataDirectory> { self.data_directories.get(id) } /// Returns the export table of this file. /// /// The export table is located using the data directory. pub fn export_table(&self) -> Result>> { self.data_directories .export_table(self.data, &self.common.sections) } /// Returns the import table of this file. /// /// The import table is located using the data directory. pub fn import_table(&self) -> Result>> { self.data_directories .import_table(self.data, &self.common.sections) } pub(super) fn section_alignment(&self) -> u64 { u64::from(self.nt_headers.optional_header().section_alignment()) } } impl<'data, Pe, R> read::private::Sealed for PeFile<'data, Pe, R> where Pe: ImageNtHeaders, R: ReadRef<'data>, { } impl<'data, Pe, R> Object<'data> for PeFile<'data, Pe, R> where Pe: ImageNtHeaders, R: ReadRef<'data>, { type Segment<'file> = PeSegment<'data, 'file, Pe, R> where Self: 'file, 'data: 'file; type SegmentIterator<'file> = PeSegmentIterator<'data, 'file, Pe, R> where Self: 'file, 'data: 'file; type Section<'file> = PeSection<'data, 'file, Pe, R> where Self: 'file, 'data: 'file; type SectionIterator<'file> = PeSectionIterator<'data, 'file, Pe, R> where Self: 'file, 'data: 'file; type Comdat<'file> = PeComdat<'data, 'file, Pe, R> where Self: 'file, 'data: 'file; type ComdatIterator<'file> = PeComdatIterator<'data, 'file, Pe, R> where Self: 'file, 'data: 'file; type Symbol<'file> = CoffSymbol<'data, 'file, R> where Self: 'file, 'data: 'file; type SymbolIterator<'file> = CoffSymbolIterator<'data, 'file, R> where Self: 'file, 'data: 'file; type SymbolTable<'file> = CoffSymbolTable<'data, 'file, R> where Self: 'file, 'data: 'file; type DynamicRelocationIterator<'file> = NoDynamicRelocationIterator where Self: 'file, 'data: 'file; fn architecture(&self) -> Architecture { match self.nt_headers.file_header().machine.get(LE) { pe::IMAGE_FILE_MACHINE_ARMNT => Architecture::Arm, pe::IMAGE_FILE_MACHINE_ARM64 | pe::IMAGE_FILE_MACHINE_ARM64EC => Architecture::Aarch64, pe::IMAGE_FILE_MACHINE_I386 => Architecture::I386, pe::IMAGE_FILE_MACHINE_AMD64 => Architecture::X86_64, _ => Architecture::Unknown, } } fn sub_architecture(&self) -> Option { match self.nt_headers.file_header().machine.get(LE) { pe::IMAGE_FILE_MACHINE_ARM64EC => Some(SubArchitecture::Arm64EC), _ => None, } } #[inline] fn is_little_endian(&self) -> bool { // Only little endian is supported. true } #[inline] fn is_64(&self) -> bool { self.nt_headers.is_type_64() } fn kind(&self) -> ObjectKind { let characteristics = self.nt_headers.file_header().characteristics.get(LE); if characteristics & pe::IMAGE_FILE_DLL != 0 { ObjectKind::Dynamic } else if characteristics & pe::IMAGE_FILE_SYSTEM != 0 { ObjectKind::Unknown } else { ObjectKind::Executable } } fn segments(&self) -> PeSegmentIterator<'data, '_, Pe, R> { PeSegmentIterator { file: self, iter: self.common.sections.iter(), } } fn section_by_name_bytes<'file>( &'file self, section_name: &[u8], ) -> Option> { self.common .sections .section_by_name(self.common.symbols.strings(), section_name) .map(|(index, section)| PeSection { file: self, index, section, }) } fn section_by_index(&self, index: SectionIndex) -> Result> { let section = self.common.sections.section(index)?; Ok(PeSection { file: self, index, section, }) } fn sections(&self) -> PeSectionIterator<'data, '_, Pe, R> { PeSectionIterator { file: self, iter: self.common.sections.iter().enumerate(), } } fn comdats(&self) -> PeComdatIterator<'data, '_, Pe, R> { PeComdatIterator { file: self } } fn symbol_by_index(&self, index: SymbolIndex) -> Result> { let symbol = self.common.symbols.symbol(index)?; Ok(CoffSymbol { file: &self.common, index, symbol, }) } fn symbols(&self) -> CoffSymbolIterator<'data, '_, R> { CoffSymbolIterator::new(&self.common) } fn symbol_table(&self) -> Option> { Some(CoffSymbolTable { file: &self.common }) } fn dynamic_symbols(&self) -> CoffSymbolIterator<'data, '_, R> { CoffSymbolIterator::empty(&self.common) } fn dynamic_symbol_table(&self) -> Option> { None } fn dynamic_relocations(&self) -> Option { None } fn imports(&self) -> Result>> { let mut imports = Vec::new(); if let Some(import_table) = self.import_table()? { let mut import_descs = import_table.descriptors()?; while let Some(import_desc) = import_descs.next()? { let library = import_table.name(import_desc.name.get(LE))?; let mut first_thunk = import_desc.original_first_thunk.get(LE); if first_thunk == 0 { first_thunk = import_desc.first_thunk.get(LE); } let mut thunks = import_table.thunks(first_thunk)?; while let Some(thunk) = thunks.next::()? { if !thunk.is_ordinal() { let (_hint, name) = import_table.hint_name(thunk.address())?; imports.push(Import { library: ByteString(library), name: ByteString(name), }); } } } } Ok(imports) } fn exports(&self) -> Result>> { let mut exports = Vec::new(); if let Some(export_table) = self.export_table()? { for (name_pointer, address_index) in export_table.name_iter() { let name = export_table.name_from_pointer(name_pointer)?; let address = export_table.address_by_index(address_index.into())?; if !export_table.is_forward(address) { exports.push(Export { name: ByteString(name), address: self.common.image_base.wrapping_add(address.into()), }) } } } Ok(exports) } fn pdb_info(&self) -> Result>> { let data_dir = match self.data_directory(pe::IMAGE_DIRECTORY_ENTRY_DEBUG) { Some(data_dir) => data_dir, None => return Ok(None), }; let debug_data = data_dir.data(self.data, &self.common.sections)?; let debug_dirs = pod::slice_from_all_bytes::(debug_data) .read_error("Invalid PE debug dir size")?; for debug_dir in debug_dirs { if debug_dir.typ.get(LE) != pe::IMAGE_DEBUG_TYPE_CODEVIEW { continue; } let info = self .data .read_slice_at::( debug_dir.pointer_to_raw_data.get(LE) as u64, debug_dir.size_of_data.get(LE) as usize, ) .read_error("Invalid CodeView Info address")?; let mut info = Bytes(info); let sig = info .read_bytes(4) .read_error("Invalid CodeView signature")?; if sig.0 != b"RSDS" { continue; } let guid: [u8; 16] = info .read_bytes(16) .read_error("Invalid CodeView GUID")? .0 .try_into() .unwrap(); let age = info.read::>().read_error("Invalid CodeView Age")?; let path = info .read_string() .read_error("Invalid CodeView file path")?; return Ok(Some(CodeView { path: ByteString(path), guid, age: age.get(LE), })); } Ok(None) } fn has_debug_symbols(&self) -> bool { self.section_by_name(".debug_info").is_some() } fn relative_address_base(&self) -> u64 { self.common.image_base } fn entry(&self) -> u64 { u64::from(self.nt_headers.optional_header().address_of_entry_point()) .wrapping_add(self.common.image_base) } fn flags(&self) -> FileFlags { FileFlags::Coff { characteristics: self.nt_headers.file_header().characteristics.get(LE), } } } /// An iterator for the COMDAT section groups in a [`PeFile32`]. pub type PeComdatIterator32<'data, 'file, R = &'data [u8]> = PeComdatIterator<'data, 'file, pe::ImageNtHeaders32, R>; /// An iterator for the COMDAT section groups in a [`PeFile64`]. pub type PeComdatIterator64<'data, 'file, R = &'data [u8]> = PeComdatIterator<'data, 'file, pe::ImageNtHeaders64, R>; /// An iterator for the COMDAT section groups in a [`PeFile`]. /// /// This is a stub that doesn't implement any functionality. #[derive(Debug)] pub struct PeComdatIterator<'data, 'file, Pe, R = &'data [u8]> where Pe: ImageNtHeaders, R: ReadRef<'data>, { #[allow(unused)] file: &'file PeFile<'data, Pe, R>, } impl<'data, 'file, Pe, R> Iterator for PeComdatIterator<'data, 'file, Pe, R> where Pe: ImageNtHeaders, R: ReadRef<'data>, { type Item = PeComdat<'data, 'file, Pe, R>; #[inline] fn next(&mut self) -> Option { None } } /// A COMDAT section group in a [`PeFile32`]. pub type PeComdat32<'data, 'file, R = &'data [u8]> = PeComdat<'data, 'file, pe::ImageNtHeaders32, R>; /// A COMDAT section group in a [`PeFile64`]. pub type PeComdat64<'data, 'file, R = &'data [u8]> = PeComdat<'data, 'file, pe::ImageNtHeaders64, R>; /// A COMDAT section group in a [`PeFile`]. /// /// This is a stub that doesn't implement any functionality. #[derive(Debug)] pub struct PeComdat<'data, 'file, Pe, R = &'data [u8]> where Pe: ImageNtHeaders, R: ReadRef<'data>, { #[allow(unused)] file: &'file PeFile<'data, Pe, R>, } impl<'data, 'file, Pe, R> read::private::Sealed for PeComdat<'data, 'file, Pe, R> where Pe: ImageNtHeaders, R: ReadRef<'data>, { } impl<'data, 'file, Pe, R> ObjectComdat<'data> for PeComdat<'data, 'file, Pe, R> where Pe: ImageNtHeaders, R: ReadRef<'data>, { type SectionIterator = PeComdatSectionIterator<'data, 'file, Pe, R>; #[inline] fn kind(&self) -> ComdatKind { unreachable!(); } #[inline] fn symbol(&self) -> SymbolIndex { unreachable!(); } #[inline] fn name_bytes(&self) -> Result<&'data [u8]> { unreachable!(); } #[inline] fn name(&self) -> Result<&'data str> { unreachable!(); } #[inline] fn sections(&self) -> Self::SectionIterator { unreachable!(); } } /// An iterator for the sections in a COMDAT section group in a [`PeFile32`]. pub type PeComdatSectionIterator32<'data, 'file, R = &'data [u8]> = PeComdatSectionIterator<'data, 'file, pe::ImageNtHeaders32, R>; /// An iterator for the sections in a COMDAT section group in a [`PeFile64`]. pub type PeComdatSectionIterator64<'data, 'file, R = &'data [u8]> = PeComdatSectionIterator<'data, 'file, pe::ImageNtHeaders64, R>; /// An iterator for the sections in a COMDAT section group in a [`PeFile`]. /// /// This is a stub that doesn't implement any functionality. #[derive(Debug)] pub struct PeComdatSectionIterator<'data, 'file, Pe, R = &'data [u8]> where Pe: ImageNtHeaders, R: ReadRef<'data>, { #[allow(unused)] file: &'file PeFile<'data, Pe, R>, } impl<'data, 'file, Pe, R> Iterator for PeComdatSectionIterator<'data, 'file, Pe, R> where Pe: ImageNtHeaders, R: ReadRef<'data>, { type Item = SectionIndex; fn next(&mut self) -> Option { None } } impl pe::ImageDosHeader { /// Read the DOS header. /// /// Also checks that the `e_magic` field in the header is valid. pub fn parse<'data, R: ReadRef<'data>>(data: R) -> read::Result<&'data Self> { // DOS header comes first. let dos_header = data .read_at::(0) .read_error("Invalid DOS header size or alignment")?; if dos_header.e_magic.get(LE) != pe::IMAGE_DOS_SIGNATURE { return Err(Error("Invalid DOS magic")); } Ok(dos_header) } /// Return the file offset of the nt_headers. #[inline] pub fn nt_headers_offset(&self) -> u32 { self.e_lfanew.get(LE) } } /// Find the optional header and read its `magic` field. /// /// It can be useful to know this magic value before trying to /// fully parse the NT headers. pub fn optional_header_magic<'data, R: ReadRef<'data>>(data: R) -> Result { let dos_header = pe::ImageDosHeader::parse(data)?; // NT headers are at an offset specified in the DOS header. let offset = dos_header.nt_headers_offset().into(); // It doesn't matter which NT header type is used for the purpose // of reading the optional header magic. let nt_headers = data .read_at::(offset) .read_error("Invalid NT headers offset, size, or alignment")?; if nt_headers.signature() != pe::IMAGE_NT_SIGNATURE { return Err(Error("Invalid PE magic")); } Ok(nt_headers.optional_header().magic()) } /// A trait for generic access to [`pe::ImageNtHeaders32`] and [`pe::ImageNtHeaders64`]. #[allow(missing_docs)] pub trait ImageNtHeaders: Debug + Pod { type ImageOptionalHeader: ImageOptionalHeader; type ImageThunkData: ImageThunkData; /// Return true if this type is a 64-bit header. /// /// This is a property of the type, not a value in the header data. fn is_type_64(&self) -> bool; /// Return true if the magic field in the optional header is valid. fn is_valid_optional_magic(&self) -> bool; /// Return the signature fn signature(&self) -> u32; /// Return the file header. fn file_header(&self) -> &pe::ImageFileHeader; /// Return the optional header. fn optional_header(&self) -> &Self::ImageOptionalHeader; // Provided methods. /// Read the NT headers, including the data directories. /// /// `data` must be for the entire file. /// /// `offset` must be headers offset, which can be obtained from [`pe::ImageDosHeader::nt_headers_offset`]. /// It is updated to point after the optional header, which is where the section headers are located. /// /// Also checks that the `signature` and `magic` fields in the headers are valid. fn parse<'data, R: ReadRef<'data>>( data: R, offset: &mut u64, ) -> read::Result<(&'data Self, DataDirectories<'data>)> { // Note that this does not include the data directories in the optional header. let nt_headers = data .read::(offset) .read_error("Invalid PE headers offset or size")?; if nt_headers.signature() != pe::IMAGE_NT_SIGNATURE { return Err(Error("Invalid PE magic")); } if !nt_headers.is_valid_optional_magic() { return Err(Error("Invalid PE optional header magic")); } // Read the rest of the optional header, and then read the data directories from that. let optional_data_size = u64::from(nt_headers.file_header().size_of_optional_header.get(LE)) .checked_sub(mem::size_of::() as u64) .read_error("PE optional header size is too small")?; let optional_data = data .read_bytes(offset, optional_data_size) .read_error("Invalid PE optional header size")?; let data_directories = DataDirectories::parse( optional_data, nt_headers.optional_header().number_of_rva_and_sizes(), )?; Ok((nt_headers, data_directories)) } /// Read the section table. /// /// `data` must be for the entire file. /// `offset` must be after the optional file header. #[inline] fn sections<'data, R: ReadRef<'data>>( &self, data: R, offset: u64, ) -> read::Result> { SectionTable::parse(self.file_header(), data, offset) } /// Read the COFF symbol table and string table. /// /// `data` must be the entire file data. #[inline] fn symbols<'data, R: ReadRef<'data>>(&self, data: R) -> read::Result> { SymbolTable::parse(self.file_header(), data) } } /// A trait for generic access to [`pe::ImageOptionalHeader32`] and [`pe::ImageOptionalHeader64`]. #[allow(missing_docs)] pub trait ImageOptionalHeader: Debug + Pod { // Standard fields. fn magic(&self) -> u16; fn major_linker_version(&self) -> u8; fn minor_linker_version(&self) -> u8; fn size_of_code(&self) -> u32; fn size_of_initialized_data(&self) -> u32; fn size_of_uninitialized_data(&self) -> u32; fn address_of_entry_point(&self) -> u32; fn base_of_code(&self) -> u32; fn base_of_data(&self) -> Option; // NT additional fields. fn image_base(&self) -> u64; fn section_alignment(&self) -> u32; fn file_alignment(&self) -> u32; fn major_operating_system_version(&self) -> u16; fn minor_operating_system_version(&self) -> u16; fn major_image_version(&self) -> u16; fn minor_image_version(&self) -> u16; fn major_subsystem_version(&self) -> u16; fn minor_subsystem_version(&self) -> u16; fn win32_version_value(&self) -> u32; fn size_of_image(&self) -> u32; fn size_of_headers(&self) -> u32; fn check_sum(&self) -> u32; fn subsystem(&self) -> u16; fn dll_characteristics(&self) -> u16; fn size_of_stack_reserve(&self) -> u64; fn size_of_stack_commit(&self) -> u64; fn size_of_heap_reserve(&self) -> u64; fn size_of_heap_commit(&self) -> u64; fn loader_flags(&self) -> u32; fn number_of_rva_and_sizes(&self) -> u32; } impl ImageNtHeaders for pe::ImageNtHeaders32 { type ImageOptionalHeader = pe::ImageOptionalHeader32; type ImageThunkData = pe::ImageThunkData32; #[inline] fn is_type_64(&self) -> bool { false } #[inline] fn is_valid_optional_magic(&self) -> bool { self.optional_header.magic.get(LE) == pe::IMAGE_NT_OPTIONAL_HDR32_MAGIC } #[inline] fn signature(&self) -> u32 { self.signature.get(LE) } #[inline] fn file_header(&self) -> &pe::ImageFileHeader { &self.file_header } #[inline] fn optional_header(&self) -> &Self::ImageOptionalHeader { &self.optional_header } } impl ImageOptionalHeader for pe::ImageOptionalHeader32 { #[inline] fn magic(&self) -> u16 { self.magic.get(LE) } #[inline] fn major_linker_version(&self) -> u8 { self.major_linker_version } #[inline] fn minor_linker_version(&self) -> u8 { self.minor_linker_version } #[inline] fn size_of_code(&self) -> u32 { self.size_of_code.get(LE) } #[inline] fn size_of_initialized_data(&self) -> u32 { self.size_of_initialized_data.get(LE) } #[inline] fn size_of_uninitialized_data(&self) -> u32 { self.size_of_uninitialized_data.get(LE) } #[inline] fn address_of_entry_point(&self) -> u32 { self.address_of_entry_point.get(LE) } #[inline] fn base_of_code(&self) -> u32 { self.base_of_code.get(LE) } #[inline] fn base_of_data(&self) -> Option { Some(self.base_of_data.get(LE)) } #[inline] fn image_base(&self) -> u64 { self.image_base.get(LE).into() } #[inline] fn section_alignment(&self) -> u32 { self.section_alignment.get(LE) } #[inline] fn file_alignment(&self) -> u32 { self.file_alignment.get(LE) } #[inline] fn major_operating_system_version(&self) -> u16 { self.major_operating_system_version.get(LE) } #[inline] fn minor_operating_system_version(&self) -> u16 { self.minor_operating_system_version.get(LE) } #[inline] fn major_image_version(&self) -> u16 { self.major_image_version.get(LE) } #[inline] fn minor_image_version(&self) -> u16 { self.minor_image_version.get(LE) } #[inline] fn major_subsystem_version(&self) -> u16 { self.major_subsystem_version.get(LE) } #[inline] fn minor_subsystem_version(&self) -> u16 { self.minor_subsystem_version.get(LE) } #[inline] fn win32_version_value(&self) -> u32 { self.win32_version_value.get(LE) } #[inline] fn size_of_image(&self) -> u32 { self.size_of_image.get(LE) } #[inline] fn size_of_headers(&self) -> u32 { self.size_of_headers.get(LE) } #[inline] fn check_sum(&self) -> u32 { self.check_sum.get(LE) } #[inline] fn subsystem(&self) -> u16 { self.subsystem.get(LE) } #[inline] fn dll_characteristics(&self) -> u16 { self.dll_characteristics.get(LE) } #[inline] fn size_of_stack_reserve(&self) -> u64 { self.size_of_stack_reserve.get(LE).into() } #[inline] fn size_of_stack_commit(&self) -> u64 { self.size_of_stack_commit.get(LE).into() } #[inline] fn size_of_heap_reserve(&self) -> u64 { self.size_of_heap_reserve.get(LE).into() } #[inline] fn size_of_heap_commit(&self) -> u64 { self.size_of_heap_commit.get(LE).into() } #[inline] fn loader_flags(&self) -> u32 { self.loader_flags.get(LE) } #[inline] fn number_of_rva_and_sizes(&self) -> u32 { self.number_of_rva_and_sizes.get(LE) } } impl ImageNtHeaders for pe::ImageNtHeaders64 { type ImageOptionalHeader = pe::ImageOptionalHeader64; type ImageThunkData = pe::ImageThunkData64; #[inline] fn is_type_64(&self) -> bool { true } #[inline] fn is_valid_optional_magic(&self) -> bool { self.optional_header.magic.get(LE) == pe::IMAGE_NT_OPTIONAL_HDR64_MAGIC } #[inline] fn signature(&self) -> u32 { self.signature.get(LE) } #[inline] fn file_header(&self) -> &pe::ImageFileHeader { &self.file_header } #[inline] fn optional_header(&self) -> &Self::ImageOptionalHeader { &self.optional_header } } impl ImageOptionalHeader for pe::ImageOptionalHeader64 { #[inline] fn magic(&self) -> u16 { self.magic.get(LE) } #[inline] fn major_linker_version(&self) -> u8 { self.major_linker_version } #[inline] fn minor_linker_version(&self) -> u8 { self.minor_linker_version } #[inline] fn size_of_code(&self) -> u32 { self.size_of_code.get(LE) } #[inline] fn size_of_initialized_data(&self) -> u32 { self.size_of_initialized_data.get(LE) } #[inline] fn size_of_uninitialized_data(&self) -> u32 { self.size_of_uninitialized_data.get(LE) } #[inline] fn address_of_entry_point(&self) -> u32 { self.address_of_entry_point.get(LE) } #[inline] fn base_of_code(&self) -> u32 { self.base_of_code.get(LE) } #[inline] fn base_of_data(&self) -> Option { None } #[inline] fn image_base(&self) -> u64 { self.image_base.get(LE) } #[inline] fn section_alignment(&self) -> u32 { self.section_alignment.get(LE) } #[inline] fn file_alignment(&self) -> u32 { self.file_alignment.get(LE) } #[inline] fn major_operating_system_version(&self) -> u16 { self.major_operating_system_version.get(LE) } #[inline] fn minor_operating_system_version(&self) -> u16 { self.minor_operating_system_version.get(LE) } #[inline] fn major_image_version(&self) -> u16 { self.major_image_version.get(LE) } #[inline] fn minor_image_version(&self) -> u16 { self.minor_image_version.get(LE) } #[inline] fn major_subsystem_version(&self) -> u16 { self.major_subsystem_version.get(LE) } #[inline] fn minor_subsystem_version(&self) -> u16 { self.minor_subsystem_version.get(LE) } #[inline] fn win32_version_value(&self) -> u32 { self.win32_version_value.get(LE) } #[inline] fn size_of_image(&self) -> u32 { self.size_of_image.get(LE) } #[inline] fn size_of_headers(&self) -> u32 { self.size_of_headers.get(LE) } #[inline] fn check_sum(&self) -> u32 { self.check_sum.get(LE) } #[inline] fn subsystem(&self) -> u16 { self.subsystem.get(LE) } #[inline] fn dll_characteristics(&self) -> u16 { self.dll_characteristics.get(LE) } #[inline] fn size_of_stack_reserve(&self) -> u64 { self.size_of_stack_reserve.get(LE) } #[inline] fn size_of_stack_commit(&self) -> u64 { self.size_of_stack_commit.get(LE) } #[inline] fn size_of_heap_reserve(&self) -> u64 { self.size_of_heap_reserve.get(LE) } #[inline] fn size_of_heap_commit(&self) -> u64 { self.size_of_heap_commit.get(LE) } #[inline] fn loader_flags(&self) -> u32 { self.loader_flags.get(LE) } #[inline] fn number_of_rva_and_sizes(&self) -> u32 { self.number_of_rva_and_sizes.get(LE) } } object-0.36.5/src/read/pe/import.rs000064400000000000000000000306121046102023000151550ustar 00000000000000use core::fmt::Debug; use core::mem; use crate::endian::{LittleEndian as LE, U16Bytes}; use crate::pe; use crate::pod::Pod; use crate::read::{Bytes, ReadError, Result}; use super::ImageNtHeaders; /// Information for parsing a PE import table. /// /// Returned by [`DataDirectories::import_table`](super::DataDirectories::import_table). #[derive(Debug, Clone)] pub struct ImportTable<'data> { section_data: Bytes<'data>, section_address: u32, import_address: u32, } impl<'data> ImportTable<'data> { /// Create a new import table parser. /// /// The import descriptors start at `import_address`. /// The size declared in the `IMAGE_DIRECTORY_ENTRY_IMPORT` data directory is /// ignored by the Windows loader, and so descriptors will be parsed until a null entry. /// /// `section_data` should be from the section containing `import_address`, and /// `section_address` should be the address of that section. Pointers within the /// descriptors and thunks may point to anywhere within the section data. pub fn new(section_data: &'data [u8], section_address: u32, import_address: u32) -> Self { ImportTable { section_data: Bytes(section_data), section_address, import_address, } } /// Return an iterator for the import descriptors. pub fn descriptors(&self) -> Result> { let offset = self.import_address.wrapping_sub(self.section_address); let mut data = self.section_data; data.skip(offset as usize) .read_error("Invalid PE import descriptor address")?; Ok(ImportDescriptorIterator { data, null: false }) } /// Return a library name given its address. /// /// This address may be from [`pe::ImageImportDescriptor::name`]. pub fn name(&self, address: u32) -> Result<&'data [u8]> { self.section_data .read_string_at(address.wrapping_sub(self.section_address) as usize) .read_error("Invalid PE import descriptor name") } /// Return a list of thunks given its address. /// /// This address may be from [`pe::ImageImportDescriptor::original_first_thunk`] /// or [`pe::ImageImportDescriptor::first_thunk`]. pub fn thunks(&self, address: u32) -> Result> { let offset = address.wrapping_sub(self.section_address); let mut data = self.section_data; data.skip(offset as usize) .read_error("Invalid PE import thunk table address")?; Ok(ImportThunkList { data }) } /// Parse a thunk. pub fn import(&self, thunk: Pe::ImageThunkData) -> Result> { if thunk.is_ordinal() { Ok(Import::Ordinal(thunk.ordinal())) } else { let (hint, name) = self.hint_name(thunk.address())?; Ok(Import::Name(hint, name)) } } /// Return the hint and name at the given address. /// /// This address may be from [`pe::ImageThunkData32`] or [`pe::ImageThunkData64`]. /// /// The hint is an index into the export name pointer table in the target library. pub fn hint_name(&self, address: u32) -> Result<(u16, &'data [u8])> { let offset = address.wrapping_sub(self.section_address); let mut data = self.section_data; data.skip(offset as usize) .read_error("Invalid PE import thunk address")?; let hint = data .read::>() .read_error("Missing PE import thunk hint")? .get(LE); let name = data .read_string() .read_error("Missing PE import thunk name")?; Ok((hint, name)) } } /// A fallible iterator for the descriptors in the import data directory. #[derive(Debug, Clone)] pub struct ImportDescriptorIterator<'data> { data: Bytes<'data>, null: bool, } impl<'data> ImportDescriptorIterator<'data> { /// Return the next descriptor. /// /// Returns `Ok(None)` when a null descriptor is found. pub fn next(&mut self) -> Result> { if self.null { return Ok(None); } let result = self .data .read::() .read_error("Missing PE null import descriptor"); match result { Ok(import_desc) => { if import_desc.is_null() { self.null = true; Ok(None) } else { Ok(Some(import_desc)) } } Err(e) => { self.null = true; Err(e) } } } } impl<'data> Iterator for ImportDescriptorIterator<'data> { type Item = Result<&'data pe::ImageImportDescriptor>; fn next(&mut self) -> Option { self.next().transpose() } } /// A list of import thunks. /// /// These may be in the import lookup table, or the import address table. #[derive(Debug, Clone)] pub struct ImportThunkList<'data> { data: Bytes<'data>, } impl<'data> ImportThunkList<'data> { /// Get the thunk at the given index. pub fn get(&self, index: usize) -> Result { let thunk = self .data .read_at(index * mem::size_of::()) .read_error("Invalid PE import thunk index")?; Ok(*thunk) } /// Return the first thunk in the list, and update `self` to point after it. /// /// Returns `Ok(None)` when a null thunk is found. pub fn next(&mut self) -> Result> { let thunk = self .data .read::() .read_error("Missing PE null import thunk")?; if thunk.address() == 0 { Ok(None) } else { Ok(Some(*thunk)) } } } /// A parsed import thunk. #[derive(Debug, Clone, Copy)] pub enum Import<'data> { /// Import by ordinal. Ordinal(u16), /// Import by name. /// /// Includes a hint for the index into the export name pointer table in the target library. Name(u16, &'data [u8]), } /// A trait for generic access to [`pe::ImageThunkData32`] and [`pe::ImageThunkData64`]. #[allow(missing_docs)] pub trait ImageThunkData: Debug + Pod { /// Return the raw thunk value. fn raw(self) -> u64; /// Returns true if the ordinal flag is set. fn is_ordinal(self) -> bool; /// Return the ordinal portion of the thunk. /// /// Does not check the ordinal flag. fn ordinal(self) -> u16; /// Return the RVA portion of the thunk. /// /// Does not check the ordinal flag. fn address(self) -> u32; } impl ImageThunkData for pe::ImageThunkData64 { fn raw(self) -> u64 { self.0.get(LE) } fn is_ordinal(self) -> bool { self.0.get(LE) & pe::IMAGE_ORDINAL_FLAG64 != 0 } fn ordinal(self) -> u16 { self.0.get(LE) as u16 } fn address(self) -> u32 { self.0.get(LE) as u32 & 0x7fff_ffff } } impl ImageThunkData for pe::ImageThunkData32 { fn raw(self) -> u64 { self.0.get(LE).into() } fn is_ordinal(self) -> bool { self.0.get(LE) & pe::IMAGE_ORDINAL_FLAG32 != 0 } fn ordinal(self) -> u16 { self.0.get(LE) as u16 } fn address(self) -> u32 { self.0.get(LE) & 0x7fff_ffff } } /// Information for parsing a PE delay-load import table. /// /// Returned by /// [`DataDirectories::delay_load_import_table`](super::DataDirectories::delay_load_import_table). #[derive(Debug, Clone)] pub struct DelayLoadImportTable<'data> { section_data: Bytes<'data>, section_address: u32, import_address: u32, } impl<'data> DelayLoadImportTable<'data> { /// Create a new delay load import table parser. /// /// The import descriptors start at `import_address`. /// This table works in the same way the import table does: descriptors will be /// parsed until a null entry. /// /// `section_data` should be from the section containing `import_address`, and /// `section_address` should be the address of that section. Pointers within the /// descriptors and thunks may point to anywhere within the section data. pub fn new(section_data: &'data [u8], section_address: u32, import_address: u32) -> Self { DelayLoadImportTable { section_data: Bytes(section_data), section_address, import_address, } } /// Return an iterator for the import descriptors. pub fn descriptors(&self) -> Result> { let offset = self.import_address.wrapping_sub(self.section_address); let mut data = self.section_data; data.skip(offset as usize) .read_error("Invalid PE delay-load import descriptor address")?; Ok(DelayLoadDescriptorIterator { data, null: false }) } /// Return a library name given its address. /// /// This address may be from [`pe::ImageDelayloadDescriptor::dll_name_rva`]. pub fn name(&self, address: u32) -> Result<&'data [u8]> { self.section_data .read_string_at(address.wrapping_sub(self.section_address) as usize) .read_error("Invalid PE import descriptor name") } /// Return a list of thunks given its address. /// /// This address may be from the INT, i.e. from /// [`pe::ImageDelayloadDescriptor::import_name_table_rva`]. /// /// Please note that others RVA values from [`pe::ImageDelayloadDescriptor`] are used /// by the delay loader at runtime to store values, and thus do not point inside the same /// section as the INT. Calling this function on those addresses will fail. pub fn thunks(&self, address: u32) -> Result> { let offset = address.wrapping_sub(self.section_address); let mut data = self.section_data; data.skip(offset as usize) .read_error("Invalid PE delay load import thunk table address")?; Ok(ImportThunkList { data }) } /// Parse a thunk. pub fn import(&self, thunk: Pe::ImageThunkData) -> Result> { if thunk.is_ordinal() { Ok(Import::Ordinal(thunk.ordinal())) } else { let (hint, name) = self.hint_name(thunk.address())?; Ok(Import::Name(hint, name)) } } /// Return the hint and name at the given address. /// /// This address may be from [`pe::ImageThunkData32`] or [`pe::ImageThunkData64`]. /// /// The hint is an index into the export name pointer table in the target library. pub fn hint_name(&self, address: u32) -> Result<(u16, &'data [u8])> { let offset = address.wrapping_sub(self.section_address); let mut data = self.section_data; data.skip(offset as usize) .read_error("Invalid PE delay load import thunk address")?; let hint = data .read::>() .read_error("Missing PE delay load import thunk hint")? .get(LE); let name = data .read_string() .read_error("Missing PE delay load import thunk name")?; Ok((hint, name)) } } /// A fallible iterator for the descriptors in the delay-load data directory. #[derive(Debug, Clone)] pub struct DelayLoadDescriptorIterator<'data> { data: Bytes<'data>, null: bool, } impl<'data> DelayLoadDescriptorIterator<'data> { /// Return the next descriptor. /// /// Returns `Ok(None)` when a null descriptor is found. pub fn next(&mut self) -> Result> { if self.null { return Ok(None); } let result = self .data .read::() .read_error("Missing PE null delay-load import descriptor"); match result { Ok(import_desc) => { if import_desc.is_null() { self.null = true; Ok(None) } else { Ok(Some(import_desc)) } } Err(e) => { self.null = true; Err(e) } } } } impl<'data> Iterator for DelayLoadDescriptorIterator<'data> { type Item = Result<&'data pe::ImageDelayloadDescriptor>; fn next(&mut self) -> Option { self.next().transpose() } } object-0.36.5/src/read/pe/mod.rs000064400000000000000000000034461046102023000144270ustar 00000000000000//! Support for reading PE files. //! //! Traits are used to abstract over the difference between PE32 and PE32+. //! The primary trait for this is [`ImageNtHeaders`]. //! //! ## High level API //! //! [`PeFile`] implements the [`Object`](crate::read::Object) trait for //! PE files. [`PeFile`] is parameterised by [`ImageNtHeaders`] to allow //! reading both PE32 and PE32+. There are type aliases for these parameters //! ([`PeFile32`] and [`PeFile64`]). //! //! ## Low level API //! //! The [`ImageNtHeaders`] trait can be directly used to parse both //! [`pe::ImageNtHeaders32`] and [`pe::ImageNtHeaders64`]. //! //! ### Example for low level API //! ```no_run //! use object::pe; //! use object::read::pe::ImageNtHeaders; //! use std::error::Error; //! use std::fs; //! //! /// Reads a file and displays the name of each section. //! fn main() -> Result<(), Box> { //! # #[cfg(feature = "std")] { //! let data = fs::read("path/to/binary")?; //! let dos_header = pe::ImageDosHeader::parse(&*data)?; //! let mut offset = dos_header.nt_headers_offset().into(); //! let (nt_headers, data_directories) = pe::ImageNtHeaders64::parse(&*data, &mut offset)?; //! let sections = nt_headers.sections(&*data, offset)?; //! let symbols = nt_headers.symbols(&*data)?; //! for section in sections.iter() { //! println!("{}", String::from_utf8_lossy(section.name(symbols.strings())?)); //! } //! # } //! Ok(()) //! } //! ``` #[cfg(doc)] use crate::pe; mod file; pub use file::*; mod section; pub use section::*; mod data_directory; pub use data_directory::*; mod export; pub use export::*; mod import; pub use import::*; mod relocation; pub use relocation::*; mod resource; pub use resource::*; mod rich; pub use rich::*; pub use super::coff::{SectionTable, SymbolTable}; object-0.36.5/src/read/pe/relocation.rs000064400000000000000000000062271046102023000160070ustar 00000000000000use core::slice; use crate::endian::{LittleEndian as LE, U16}; use crate::pe; use crate::read::{Bytes, Error, ReadError, Result}; /// An iterator over the relocation blocks in the `.reloc` section of a PE file. /// /// Returned by [`DataDirectories::relocation_blocks`](super::DataDirectories::relocation_blocks). #[derive(Debug, Default, Clone, Copy)] pub struct RelocationBlockIterator<'data> { data: Bytes<'data>, } impl<'data> RelocationBlockIterator<'data> { /// Construct a new iterator from the data of the `.reloc` section. pub fn new(data: &'data [u8]) -> Self { RelocationBlockIterator { data: Bytes(data) } } /// Read the next relocation page. pub fn next(&mut self) -> Result>> { if self.data.is_empty() { return Ok(None); } let result = self.parse().map(Some); if result.is_err() { self.data = Bytes(&[]); } result } fn parse(&mut self) -> Result> { let header = self .data .read::() .read_error("Invalid PE reloc section size")?; let virtual_address = header.virtual_address.get(LE); let size = header.size_of_block.get(LE); if size <= 8 || size & 3 != 0 { return Err(Error("Invalid PE reloc block size")); } let count = (size - 8) / 2; let relocs = self .data .read_slice::>(count as usize) .read_error("Invalid PE reloc block size")? .iter(); Ok(RelocationIterator { virtual_address, size, relocs, }) } } impl<'data> Iterator for RelocationBlockIterator<'data> { type Item = Result>; fn next(&mut self) -> Option { self.next().transpose() } } /// An iterator of the relocations in a block in the `.reloc` section of a PE file. #[derive(Debug, Clone)] pub struct RelocationIterator<'data> { virtual_address: u32, size: u32, relocs: slice::Iter<'data, U16>, } impl<'data> RelocationIterator<'data> { /// Return the virtual address of the page that this block of relocations applies to. pub fn virtual_address(&self) -> u32 { self.virtual_address } /// Return the size in bytes of this block of relocations. pub fn size(&self) -> u32 { self.size } } impl<'data> Iterator for RelocationIterator<'data> { type Item = Relocation; fn next(&mut self) -> Option { loop { let reloc = self.relocs.next()?.get(LE); if reloc != 0 { return Some(Relocation { virtual_address: self.virtual_address.wrapping_add((reloc & 0xfff) as u32), typ: reloc >> 12, }); } } } } /// A relocation in the `.reloc` section of a PE file. #[derive(Debug, Default, Clone, Copy)] pub struct Relocation { /// The virtual address of the relocation. pub virtual_address: u32, /// One of the `pe::IMAGE_REL_BASED_*` constants. pub typ: u16, } object-0.36.5/src/read/pe/resource.rs000064400000000000000000000142301046102023000154700ustar 00000000000000use alloc::string::String; use core::char; use crate::endian::{LittleEndian as LE, U16Bytes}; use crate::pe; use crate::read::{ReadError, ReadRef, Result}; /// The `.rsrc` section of a PE file. /// /// Returned by [`DataDirectories::resource_directory`](super::DataDirectories::resource_directory). #[derive(Debug, Clone, Copy)] pub struct ResourceDirectory<'data> { data: &'data [u8], } impl<'data> ResourceDirectory<'data> { /// Construct from the data of the `.rsrc` section. pub fn new(data: &'data [u8]) -> Self { ResourceDirectory { data } } /// Parses the root resource directory. pub fn root(&self) -> Result> { ResourceDirectoryTable::parse(self.data, 0) } } /// A table of resource entries. #[derive(Debug, Clone)] pub struct ResourceDirectoryTable<'data> { /// The table header. pub header: &'data pe::ImageResourceDirectory, /// The table entries. pub entries: &'data [pe::ImageResourceDirectoryEntry], } impl<'data> ResourceDirectoryTable<'data> { fn parse(data: &'data [u8], offset: u32) -> Result { let mut offset = u64::from(offset); let header = data .read::(&mut offset) .read_error("Invalid resource table header")?; let entries_count = header.number_of_id_entries.get(LE) as usize + header.number_of_named_entries.get(LE) as usize; let entries = data .read_slice::(&mut offset, entries_count) .read_error("Invalid resource table entries")?; Ok(Self { header, entries }) } } impl pe::ImageResourceDirectoryEntry { /// Returns true if the entry has a name, rather than an ID. pub fn has_name(&self) -> bool { self.name_or_id.get(LE) & pe::IMAGE_RESOURCE_NAME_IS_STRING != 0 } /// Returns the section offset of the name. /// /// Valid if `has_name()` returns true. fn name(&self) -> ResourceName { let offset = self.name_or_id.get(LE) & !pe::IMAGE_RESOURCE_NAME_IS_STRING; ResourceName { offset } } /// Returns the ID. /// /// Valid if `has_string_name()` returns false. fn id(&self) -> u16 { (self.name_or_id.get(LE) & 0x0000_FFFF) as u16 } /// Returns the entry name pub fn name_or_id(&self) -> ResourceNameOrId { if self.has_name() { ResourceNameOrId::Name(self.name()) } else { ResourceNameOrId::Id(self.id()) } } /// Returns true if the entry is a subtable. pub fn is_table(&self) -> bool { self.offset_to_data_or_directory.get(LE) & pe::IMAGE_RESOURCE_DATA_IS_DIRECTORY != 0 } /// Returns the section offset of the associated table or data. pub fn data_offset(&self) -> u32 { self.offset_to_data_or_directory.get(LE) & !pe::IMAGE_RESOURCE_DATA_IS_DIRECTORY } /// Returns the data associated to this directory entry. pub fn data<'data>( &self, section: ResourceDirectory<'data>, ) -> Result> { if self.is_table() { ResourceDirectoryTable::parse(section.data, self.data_offset()) .map(ResourceDirectoryEntryData::Table) } else { section .data .read_at::(self.data_offset().into()) .read_error("Invalid resource entry") .map(ResourceDirectoryEntryData::Data) } } } /// Data associated with a resource directory entry. #[derive(Debug, Clone)] pub enum ResourceDirectoryEntryData<'data> { /// A subtable entry. Table(ResourceDirectoryTable<'data>), /// A resource data entry. Data(&'data pe::ImageResourceDataEntry), } impl<'data> ResourceDirectoryEntryData<'data> { /// Converts to an option of table. /// /// Helper for iterator filtering. pub fn table(self) -> Option> { match self { Self::Table(dir) => Some(dir), _ => None, } } /// Converts to an option of data entry. /// /// Helper for iterator filtering. pub fn data(self) -> Option<&'data pe::ImageResourceDataEntry> { match self { Self::Data(rsc) => Some(rsc), _ => None, } } } /// A resource name. #[derive(Debug, Clone, Copy)] pub struct ResourceName { offset: u32, } impl ResourceName { /// Converts to a `String`. pub fn to_string_lossy(&self, directory: ResourceDirectory<'_>) -> Result { let d = self.data(directory)?.iter().map(|c| c.get(LE)); Ok(char::decode_utf16(d) .map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER)) .collect::()) } /// Returns the string unicode buffer. pub fn data<'data>( &self, directory: ResourceDirectory<'data>, ) -> Result<&'data [U16Bytes]> { let mut offset = u64::from(self.offset); let len = directory .data .read::>(&mut offset) .read_error("Invalid resource name offset")?; directory .data .read_slice::>(&mut offset, len.get(LE).into()) .read_error("Invalid resource name length") } /// Returns the string buffer as raw bytes. pub fn raw_data<'data>(&self, directory: ResourceDirectory<'data>) -> Result<&'data [u8]> { self.data(directory).map(crate::pod::bytes_of_slice) } } /// A resource name or ID. /// /// Can be either a string or a numeric ID. #[derive(Debug)] pub enum ResourceNameOrId { /// A resource name. Name(ResourceName), /// A resource ID. Id(u16), } impl ResourceNameOrId { /// Converts to an option of name. /// /// Helper for iterator filtering. pub fn name(self) -> Option { match self { Self::Name(name) => Some(name), _ => None, } } /// Converts to an option of ID. /// /// Helper for iterator filtering. pub fn id(self) -> Option { match self { Self::Id(id) => Some(id), _ => None, } } } object-0.36.5/src/read/pe/rich.rs000064400000000000000000000065621046102023000145770ustar 00000000000000//! PE rich header handling use core::mem; use crate::endian::{LittleEndian as LE, U32}; use crate::pe; use crate::pod::bytes_of_slice; use crate::read::{Bytes, ReadRef}; /// Parsed information about a Rich Header. #[derive(Debug, Clone, Copy)] pub struct RichHeaderInfo<'data> { /// The offset at which the rich header starts. pub offset: usize, /// The length (in bytes) of the rich header. /// /// This includes the payload, but also the 16-byte start sequence and the /// 8-byte final "Rich" and XOR key. pub length: usize, /// The XOR key used to mask the rich header. /// /// Unless the file has been tampered with, it should be equal to a checksum /// of the file header. pub xor_key: u32, masked_entries: &'data [pe::MaskedRichHeaderEntry], } /// A PE rich header entry after it has been unmasked. /// /// See [`pe::MaskedRichHeaderEntry`]. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct RichHeaderEntry { /// ID of the component. pub comp_id: u32, /// Number of times this component has been used when building this PE. pub count: u32, } impl<'data> RichHeaderInfo<'data> { /// Try to locate a rich header and its entries in the current PE file. pub fn parse>(data: R, nt_header_offset: u64) -> Option { // Locate the rich header, if any. // It ends with the "Rich" string and an XOR key, before the NT header. let data = data.read_bytes_at(0, nt_header_offset).map(Bytes).ok()?; let end_marker_offset = memmem(data.0, b"Rich", 4)?; let xor_key = *data.read_at::>(end_marker_offset + 4).ok()?; // It starts at the masked "DanS" string and 3 masked zeroes. let masked_start_marker = U32::new(LE, 0x536e_6144 ^ xor_key.get(LE)); let start_header = [masked_start_marker, xor_key, xor_key, xor_key]; let start_sequence = bytes_of_slice(&start_header); let start_marker_offset = memmem(&data.0[..end_marker_offset], start_sequence, 4)?; // Extract the items between the markers. let items_offset = start_marker_offset + start_sequence.len(); let items_len = end_marker_offset - items_offset; let item_count = items_len / mem::size_of::(); let items = data.read_slice_at(items_offset, item_count).ok()?; Some(RichHeaderInfo { offset: start_marker_offset, // Includes "Rich" marker and the XOR key. length: end_marker_offset - start_marker_offset + 8, xor_key: xor_key.get(LE), masked_entries: items, }) } /// Returns an iterator over the unmasked entries. pub fn unmasked_entries(&self) -> impl Iterator + 'data { let xor_key = self.xor_key; self.masked_entries .iter() .map(move |entry| RichHeaderEntry { comp_id: entry.masked_comp_id.get(LE) ^ xor_key, count: entry.masked_count.get(LE) ^ xor_key, }) } } /// Find the offset of the first occurrence of needle in the data. /// /// The offset must have the given alignment. fn memmem(data: &[u8], needle: &[u8], align: usize) -> Option { let mut offset = 0; loop { if data.get(offset..)?.get(..needle.len())? == needle { return Some(offset); } offset += align; } } object-0.36.5/src/read/pe/section.rs000064400000000000000000000341271046102023000153140ustar 00000000000000use core::marker::PhantomData; use core::{cmp, iter, slice, str}; use crate::endian::LittleEndian as LE; use crate::pe; use crate::pe::ImageSectionHeader; use crate::read::{ self, CompressedData, CompressedFileRange, ObjectSection, ObjectSegment, ReadError, ReadRef, Relocation, RelocationMap, Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags, }; use super::{ImageNtHeaders, PeFile, SectionTable}; /// An iterator for the loadable sections in a [`PeFile32`](super::PeFile32). pub type PeSegmentIterator32<'data, 'file, R = &'data [u8]> = PeSegmentIterator<'data, 'file, pe::ImageNtHeaders32, R>; /// An iterator for the loadable sections in a [`PeFile64`](super::PeFile64). pub type PeSegmentIterator64<'data, 'file, R = &'data [u8]> = PeSegmentIterator<'data, 'file, pe::ImageNtHeaders64, R>; /// An iterator for the loadable sections in a [`PeFile`]. #[derive(Debug)] pub struct PeSegmentIterator<'data, 'file, Pe, R = &'data [u8]> where Pe: ImageNtHeaders, R: ReadRef<'data>, { pub(super) file: &'file PeFile<'data, Pe, R>, pub(super) iter: slice::Iter<'data, pe::ImageSectionHeader>, } impl<'data, 'file, Pe, R> Iterator for PeSegmentIterator<'data, 'file, Pe, R> where Pe: ImageNtHeaders, R: ReadRef<'data>, { type Item = PeSegment<'data, 'file, Pe, R>; fn next(&mut self) -> Option { self.iter.next().map(|section| PeSegment { file: self.file, section, }) } } /// A loadable section in a [`PeFile32`](super::PeFile32). pub type PeSegment32<'data, 'file, R = &'data [u8]> = PeSegment<'data, 'file, pe::ImageNtHeaders32, R>; /// A loadable section in a [`PeFile64`](super::PeFile64). pub type PeSegment64<'data, 'file, R = &'data [u8]> = PeSegment<'data, 'file, pe::ImageNtHeaders64, R>; /// A loadable section in a [`PeFile`]. /// /// Most functionality is provided by the [`ObjectSegment`] trait implementation. #[derive(Debug)] pub struct PeSegment<'data, 'file, Pe, R = &'data [u8]> where Pe: ImageNtHeaders, R: ReadRef<'data>, { file: &'file PeFile<'data, Pe, R>, section: &'data pe::ImageSectionHeader, } impl<'data, 'file, Pe, R> PeSegment<'data, 'file, Pe, R> where Pe: ImageNtHeaders, R: ReadRef<'data>, { /// Get the PE file containing this segment. pub fn pe_file(&self) -> &'file PeFile<'data, Pe, R> { self.file } /// Get the raw PE section header. pub fn pe_section(&self) -> &'data pe::ImageSectionHeader { self.section } } impl<'data, 'file, Pe, R> read::private::Sealed for PeSegment<'data, 'file, Pe, R> where Pe: ImageNtHeaders, R: ReadRef<'data>, { } impl<'data, 'file, Pe, R> ObjectSegment<'data> for PeSegment<'data, 'file, Pe, R> where Pe: ImageNtHeaders, R: ReadRef<'data>, { #[inline] fn address(&self) -> u64 { u64::from(self.section.virtual_address.get(LE)).wrapping_add(self.file.common.image_base) } #[inline] fn size(&self) -> u64 { u64::from(self.section.virtual_size.get(LE)) } #[inline] fn align(&self) -> u64 { self.file.section_alignment() } #[inline] fn file_range(&self) -> (u64, u64) { let (offset, size) = self.section.pe_file_range(); (u64::from(offset), u64::from(size)) } fn data(&self) -> Result<&'data [u8]> { self.section.pe_data(self.file.data) } fn data_range(&self, address: u64, size: u64) -> Result> { Ok(read::util::data_range( self.data()?, self.address(), address, size, )) } #[inline] fn name_bytes(&self) -> Result> { self.section .name(self.file.common.symbols.strings()) .map(Some) } #[inline] fn name(&self) -> Result> { let name = self.section.name(self.file.common.symbols.strings())?; Ok(Some( str::from_utf8(name) .ok() .read_error("Non UTF-8 PE section name")?, )) } #[inline] fn flags(&self) -> SegmentFlags { let characteristics = self.section.characteristics.get(LE); SegmentFlags::Coff { characteristics } } } /// An iterator for the sections in a [`PeFile32`](super::PeFile32). pub type PeSectionIterator32<'data, 'file, R = &'data [u8]> = PeSectionIterator<'data, 'file, pe::ImageNtHeaders32, R>; /// An iterator for the sections in a [`PeFile64`](super::PeFile64). pub type PeSectionIterator64<'data, 'file, R = &'data [u8]> = PeSectionIterator<'data, 'file, pe::ImageNtHeaders64, R>; /// An iterator for the sections in a [`PeFile`]. #[derive(Debug)] pub struct PeSectionIterator<'data, 'file, Pe, R = &'data [u8]> where Pe: ImageNtHeaders, R: ReadRef<'data>, { pub(super) file: &'file PeFile<'data, Pe, R>, pub(super) iter: iter::Enumerate>, } impl<'data, 'file, Pe, R> Iterator for PeSectionIterator<'data, 'file, Pe, R> where Pe: ImageNtHeaders, R: ReadRef<'data>, { type Item = PeSection<'data, 'file, Pe, R>; fn next(&mut self) -> Option { self.iter.next().map(|(index, section)| PeSection { file: self.file, index: SectionIndex(index + 1), section, }) } } /// A section in a [`PeFile32`](super::PeFile32). pub type PeSection32<'data, 'file, R = &'data [u8]> = PeSection<'data, 'file, pe::ImageNtHeaders32, R>; /// A section in a [`PeFile64`](super::PeFile64). pub type PeSection64<'data, 'file, R = &'data [u8]> = PeSection<'data, 'file, pe::ImageNtHeaders64, R>; /// A section in a [`PeFile`]. /// /// Most functionality is provided by the [`ObjectSection`] trait implementation. #[derive(Debug)] pub struct PeSection<'data, 'file, Pe, R = &'data [u8]> where Pe: ImageNtHeaders, R: ReadRef<'data>, { pub(super) file: &'file PeFile<'data, Pe, R>, pub(super) index: SectionIndex, pub(super) section: &'data pe::ImageSectionHeader, } impl<'data, 'file, Pe, R> PeSection<'data, 'file, Pe, R> where Pe: ImageNtHeaders, R: ReadRef<'data>, { /// Get the PE file containing this segment. pub fn pe_file(&self) -> &'file PeFile<'data, Pe, R> { self.file } /// Get the raw PE section header. pub fn pe_section(&self) -> &'data pe::ImageSectionHeader { self.section } } impl<'data, 'file, Pe, R> read::private::Sealed for PeSection<'data, 'file, Pe, R> where Pe: ImageNtHeaders, R: ReadRef<'data>, { } impl<'data, 'file, Pe, R> ObjectSection<'data> for PeSection<'data, 'file, Pe, R> where Pe: ImageNtHeaders, R: ReadRef<'data>, { type RelocationIterator = PeRelocationIterator<'data, 'file, R>; #[inline] fn index(&self) -> SectionIndex { self.index } #[inline] fn address(&self) -> u64 { u64::from(self.section.virtual_address.get(LE)).wrapping_add(self.file.common.image_base) } #[inline] fn size(&self) -> u64 { u64::from(self.section.virtual_size.get(LE)) } #[inline] fn align(&self) -> u64 { self.file.section_alignment() } #[inline] fn file_range(&self) -> Option<(u64, u64)> { let (offset, size) = self.section.pe_file_range(); if size == 0 { None } else { Some((u64::from(offset), u64::from(size))) } } fn data(&self) -> Result<&'data [u8]> { self.section.pe_data(self.file.data) } fn data_range(&self, address: u64, size: u64) -> Result> { Ok(read::util::data_range( self.data()?, self.address(), address, size, )) } #[inline] fn compressed_file_range(&self) -> Result { Ok(CompressedFileRange::none(self.file_range())) } #[inline] fn compressed_data(&self) -> Result> { self.data().map(CompressedData::none) } #[inline] fn name_bytes(&self) -> Result<&'data [u8]> { self.section.name(self.file.common.symbols.strings()) } #[inline] fn name(&self) -> Result<&'data str> { let name = self.name_bytes()?; str::from_utf8(name) .ok() .read_error("Non UTF-8 PE section name") } #[inline] fn segment_name_bytes(&self) -> Result> { Ok(None) } #[inline] fn segment_name(&self) -> Result> { Ok(None) } #[inline] fn kind(&self) -> SectionKind { self.section.kind() } fn relocations(&self) -> PeRelocationIterator<'data, 'file, R> { PeRelocationIterator(PhantomData) } fn relocation_map(&self) -> read::Result { RelocationMap::new(self.file, self) } fn flags(&self) -> SectionFlags { SectionFlags::Coff { characteristics: self.section.characteristics.get(LE), } } } impl<'data> SectionTable<'data> { /// Return the file offset of the given virtual address, and the size up /// to the end of the section containing it. /// /// Returns `None` if no section contains the address. pub fn pe_file_range_at(&self, va: u32) -> Option<(u32, u32)> { self.iter().find_map(|section| section.pe_file_range_at(va)) } /// Return the data starting at the given virtual address, up to the end of the /// section containing it. /// /// Ignores sections with invalid data. /// /// Returns `None` if no section contains the address. pub fn pe_data_at>(&self, data: R, va: u32) -> Option<&'data [u8]> { self.iter().find_map(|section| section.pe_data_at(data, va)) } /// Return the data of the section that contains the given virtual address in a PE file. /// /// Also returns the virtual address of that section. /// /// Ignores sections with invalid data. pub fn pe_data_containing>( &self, data: R, va: u32, ) -> Option<(&'data [u8], u32)> { self.iter() .find_map(|section| section.pe_data_containing(data, va)) } /// Return the section that contains a given virtual address. pub fn section_containing(&self, va: u32) -> Option<&'data ImageSectionHeader> { self.iter().find(|section| section.contains_rva(va)) } } impl pe::ImageSectionHeader { /// Return the offset and size of the section in a PE file. /// /// The size of the range will be the minimum of the file size and virtual size. pub fn pe_file_range(&self) -> (u32, u32) { // Pointer and size will be zero for uninitialized data; we don't need to validate this. let offset = self.pointer_to_raw_data.get(LE); let size = cmp::min(self.virtual_size.get(LE), self.size_of_raw_data.get(LE)); (offset, size) } /// Return the file offset of the given virtual address, and the remaining size up /// to the end of the section. /// /// Returns `None` if the section does not contain the address. pub fn pe_file_range_at(&self, va: u32) -> Option<(u32, u32)> { let section_va = self.virtual_address.get(LE); let offset = va.checked_sub(section_va)?; let (section_offset, section_size) = self.pe_file_range(); // Address must be within section (and not at its end). if offset < section_size { Some((section_offset.checked_add(offset)?, section_size - offset)) } else { None } } /// Return the virtual address and size of the section. pub fn pe_address_range(&self) -> (u32, u32) { (self.virtual_address.get(LE), self.virtual_size.get(LE)) } /// Return the section data in a PE file. /// /// The length of the data will be the minimum of the file size and virtual size. pub fn pe_data<'data, R: ReadRef<'data>>(&self, data: R) -> Result<&'data [u8]> { let (offset, size) = self.pe_file_range(); data.read_bytes_at(offset.into(), size.into()) .read_error("Invalid PE section offset or size") } /// Return the data starting at the given virtual address, up to the end of the /// section. /// /// Ignores sections with invalid data. /// /// Returns `None` if the section does not contain the address. pub fn pe_data_at<'data, R: ReadRef<'data>>(&self, data: R, va: u32) -> Option<&'data [u8]> { let (offset, size) = self.pe_file_range_at(va)?; data.read_bytes_at(offset.into(), size.into()).ok() } /// Tests whether a given RVA is part of this section pub fn contains_rva(&self, va: u32) -> bool { let section_va = self.virtual_address.get(LE); match va.checked_sub(section_va) { None => false, Some(offset) => { // Address must be within section (and not at its end). offset < self.virtual_size.get(LE) } } } /// Return the section data if it contains the given virtual address. /// /// Also returns the virtual address of that section. /// /// Ignores sections with invalid data. pub fn pe_data_containing<'data, R: ReadRef<'data>>( &self, data: R, va: u32, ) -> Option<(&'data [u8], u32)> { let section_va = self.virtual_address.get(LE); let offset = va.checked_sub(section_va)?; let (section_offset, section_size) = self.pe_file_range(); // Address must be within section (and not at its end). if offset < section_size { let section_data = data .read_bytes_at(section_offset.into(), section_size.into()) .ok()?; Some((section_data, section_va)) } else { None } } } /// An iterator for the relocations in an [`PeSection`]. /// /// This is a stub that doesn't implement any functionality. #[derive(Debug)] pub struct PeRelocationIterator<'data, 'file, R = &'data [u8]>( PhantomData<(&'data (), &'file (), R)>, ); impl<'data, 'file, R> Iterator for PeRelocationIterator<'data, 'file, R> { type Item = (u64, Relocation); fn next(&mut self) -> Option { None } } object-0.36.5/src/read/read_cache.rs000064400000000000000000000205461046102023000153020ustar 00000000000000use alloc::boxed::Box; use alloc::vec::Vec; use core::cell::RefCell; use core::convert::TryInto; use core::mem; use core::ops::Range; #[cfg(feature = "std")] use std::io::{Read, Seek, SeekFrom}; #[cfg(not(feature = "std"))] use alloc::collections::btree_map::{BTreeMap as Map, Entry}; #[cfg(feature = "std")] use std::collections::hash_map::{Entry, HashMap as Map}; use crate::read::ReadRef; /// An implementation of [`ReadRef`] for data in a stream that implements /// `Read + Seek`. /// /// Contains a cache of read-only blocks of data, allowing references to /// them to be returned. Entries in the cache are never removed. /// Entries are keyed on the offset and size of the read. /// Currently overlapping reads are considered separate reads. /// /// This is primarily intended for environments where memory mapped files /// are not available or not suitable, such as WebAssembly. /// /// Note that malformed files can cause the cache to grow much larger than /// the file size. #[derive(Debug)] pub struct ReadCache { cache: RefCell>, } #[derive(Debug)] struct ReadCacheInternal { read: R, bufs: Map<(u64, u64), Box<[u8]>>, strings: Map<(u64, u8), Box<[u8]>>, len: Option, } impl ReadCacheInternal { /// Ensures this range is contained in the len of the file fn range_in_bounds(&mut self, range: &Range) -> Result<(), ()> { if range.start <= range.end && range.end <= self.len()? { Ok(()) } else { Err(()) } } /// The length of the underlying read, memoized fn len(&mut self) -> Result { match self.len { Some(len) => Ok(len), None => { let len = self.read.len()?; self.len = Some(len); Ok(len) } } } } impl ReadCache { /// Create an empty `ReadCache` for the given stream. pub fn new(read: R) -> Self { ReadCache { cache: RefCell::new(ReadCacheInternal { read, bufs: Map::new(), strings: Map::new(), len: None, }), } } /// Return an implementation of `ReadRef` that restricts reads /// to the given range of the stream. pub fn range(&self, offset: u64, size: u64) -> ReadCacheRange<'_, R> { ReadCacheRange { r: self, offset, size, } } /// Free buffers used by the cache. pub fn clear(&mut self) { self.cache.borrow_mut().bufs.clear(); } /// Unwrap this `ReadCache`, returning the underlying reader. pub fn into_inner(self) -> R { self.cache.into_inner().read } } impl<'a, R: ReadCacheOps> ReadRef<'a> for &'a ReadCache { fn len(self) -> Result { self.cache.borrow_mut().len() } fn read_bytes_at(self, offset: u64, size: u64) -> Result<&'a [u8], ()> { if size == 0 { return Ok(&[]); } let cache = &mut *self.cache.borrow_mut(); cache.range_in_bounds(&(offset..(offset.saturating_add(size))))?; let buf = match cache.bufs.entry((offset, size)) { Entry::Occupied(entry) => entry.into_mut(), Entry::Vacant(entry) => { let size = size.try_into().map_err(|_| ())?; cache.read.seek(offset)?; let mut bytes = Vec::new(); bytes.try_reserve_exact(size).map_err(|_| ())?; bytes.resize(size, 0); let mut bytes = bytes.into_boxed_slice(); cache.read.read_exact(&mut bytes)?; entry.insert(bytes) } }; // Extend the lifetime to that of self. // This is OK because we never mutate or remove entries. Ok(unsafe { mem::transmute::<&[u8], &[u8]>(buf) }) } fn read_bytes_at_until(self, range: Range, delimiter: u8) -> Result<&'a [u8], ()> { let cache = &mut *self.cache.borrow_mut(); cache.range_in_bounds(&range)?; let buf = match cache.strings.entry((range.start, delimiter)) { Entry::Occupied(entry) => entry.into_mut(), Entry::Vacant(entry) => { cache.read.seek(range.start)?; let max_check: usize = (range.end - range.start).try_into().map_err(|_| ())?; // Strings should be relatively small. // TODO: make this configurable? let max_check = max_check.min(4096); let mut bytes = Vec::new(); let mut checked = 0; loop { bytes.resize((checked + 256).min(max_check), 0); let read = cache.read.read(&mut bytes[checked..])?; if read == 0 { return Err(()); } if let Some(len) = memchr::memchr(delimiter, &bytes[checked..][..read]) { bytes.truncate(checked + len); break entry.insert(bytes.into_boxed_slice()); } checked += read; if checked >= max_check { return Err(()); } } } }; // Extend the lifetime to that of self. // This is OK because we never mutate or remove entries. Ok(unsafe { mem::transmute::<&[u8], &[u8]>(buf) }) } } /// An implementation of [`ReadRef`] for a range of data in a stream that /// implements `Read + Seek`. /// /// Shares an underlying [`ReadCache`] with a lifetime of `'a`. #[derive(Debug)] pub struct ReadCacheRange<'a, R: ReadCacheOps> { r: &'a ReadCache, offset: u64, size: u64, } impl<'a, R: ReadCacheOps> Clone for ReadCacheRange<'a, R> { fn clone(&self) -> Self { *self } } impl<'a, R: ReadCacheOps> Copy for ReadCacheRange<'a, R> {} impl<'a, R: ReadCacheOps> ReadRef<'a> for ReadCacheRange<'a, R> { fn len(self) -> Result { Ok(self.size) } fn read_bytes_at(self, offset: u64, size: u64) -> Result<&'a [u8], ()> { if size == 0 { return Ok(&[]); } let end = offset.checked_add(size).ok_or(())?; if end > self.size { return Err(()); } let r_offset = self.offset.checked_add(offset).ok_or(())?; self.r.read_bytes_at(r_offset, size) } fn read_bytes_at_until(self, range: Range, delimiter: u8) -> Result<&'a [u8], ()> { let r_start = self.offset.checked_add(range.start).ok_or(())?; let r_end = self.offset.checked_add(range.end).ok_or(())?; let bytes = self.r.read_bytes_at_until(r_start..r_end, delimiter)?; let size = bytes.len().try_into().map_err(|_| ())?; let end = range.start.checked_add(size).ok_or(())?; if end > self.size { return Err(()); } Ok(bytes) } } /// Operations required to implement [`ReadCache`]. /// /// This is a subset of the `Read` and `Seek` traits. /// A blanket implementation is provided for all types that implement /// `Read + Seek`. #[allow(clippy::len_without_is_empty)] pub trait ReadCacheOps { /// Return the length of the stream. /// /// Equivalent to `std::io::Seek::seek(SeekFrom::End(0))`. fn len(&mut self) -> Result; /// Seek to the given position in the stream. /// /// Equivalent to `std::io::Seek::seek` with `SeekFrom::Start(pos)`. fn seek(&mut self, pos: u64) -> Result; /// Read up to `buf.len()` bytes into `buf`. /// /// Equivalent to `std::io::Read::read`. fn read(&mut self, buf: &mut [u8]) -> Result; /// Read exactly `buf.len()` bytes into `buf`. /// /// Equivalent to `std::io::Read::read_exact`. fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), ()>; } #[cfg(feature = "std")] impl ReadCacheOps for T { fn len(&mut self) -> Result { self.seek(SeekFrom::End(0)).map_err(|_| ()) } fn seek(&mut self, pos: u64) -> Result { self.seek(SeekFrom::Start(pos)).map_err(|_| ()) } fn read(&mut self, buf: &mut [u8]) -> Result { Read::read(self, buf).map_err(|_| ()) } fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), ()> { Read::read_exact(self, buf).map_err(|_| ()) } } object-0.36.5/src/read/read_ref.rs000064400000000000000000000140111046102023000150010ustar 00000000000000#![allow(clippy::len_without_is_empty)] use core::convert::TryInto; use core::ops::Range; use core::{mem, result}; use crate::pod::{from_bytes, slice_from_bytes, Pod}; type Result = result::Result; /// A trait for reading references to [`Pod`] types from a block of data. /// /// This allows parsers to handle both of these cases: /// - the block of data exists in memory, and it is desirable /// to use references to this block instead of copying it, /// - the block of data exists in storage, and it is desirable /// to read on demand to minimize I/O and memory usage. /// /// A block of data typically exists in memory as a result of using a memory /// mapped file, and the crate was written with this use case in mind. /// Reading the entire file into a `Vec` is also possible, but it often uses /// more I/O and memory. /// Both of these are handled by the `ReadRef` implementation for `&[u8]`. /// /// For the second use case, the `ReadRef` trait is implemented for /// [`&ReadCache`](super::ReadCache). This is useful for environments where /// memory mapped files are not available or not suitable, such as WebAssembly. /// This differs from reading into a `Vec` in that it only reads the portions /// of the file that are needed for parsing. /// /// The methods accept `self` by value because `Self` is expected to behave /// similar to a reference: it may be a reference with a lifetime of `'a`, /// or it may be a wrapper of a reference. /// /// The `Clone` and `Copy` bounds are for convenience, and since `Self` is /// expected to be similar to a reference, these are easily satisfied. /// /// Object file parsers typically use offsets to locate the structures /// in the block, and will most commonly use the `*_at` methods to /// read a structure at a known offset. /// /// Occasionally file parsers will need to treat the block as a stream, /// and so convenience methods are provided that update an offset with /// the size that was read. // // An alternative would be for methods to accept `&mut self` and use a // `seek` method instead of the `offset` parameters, but this is less // convenient for implementers. pub trait ReadRef<'a>: Clone + Copy { /// The total size of the block of data. fn len(self) -> Result; /// Get a reference to a `u8` slice at the given offset. /// /// Returns an error if offset or size are out of bounds. fn read_bytes_at(self, offset: u64, size: u64) -> Result<&'a [u8]>; /// Get a reference to a delimited `u8` slice which starts at range.start. /// /// Does not include the delimiter. /// /// Returns an error if the range is out of bounds or the delimiter is /// not found in the range. fn read_bytes_at_until(self, range: Range, delimiter: u8) -> Result<&'a [u8]>; /// Get a reference to a `u8` slice at the given offset, and update the offset. /// /// Returns an error if offset or size are out of bounds. fn read_bytes(self, offset: &mut u64, size: u64) -> Result<&'a [u8]> { let bytes = self.read_bytes_at(*offset, size)?; *offset = offset.wrapping_add(size); Ok(bytes) } /// Get a reference to a `Pod` type at the given offset, and update the offset. /// /// Returns an error if offset or size are out of bounds. /// /// The default implementation uses `read_bytes`, and returns an error if /// `read_bytes` does not return bytes with the correct alignment for `T`. /// Implementors may want to provide their own implementation that ensures /// the alignment can be satisfied. Alternatively, only use this method with /// types that do not need alignment (see the `unaligned` feature of this crate). fn read(self, offset: &mut u64) -> Result<&'a T> { let size = mem::size_of::().try_into().map_err(|_| ())?; let bytes = self.read_bytes(offset, size)?; let (t, _) = from_bytes(bytes)?; Ok(t) } /// Get a reference to a `Pod` type at the given offset. /// /// Returns an error if offset or size are out of bounds. /// /// Also see the `read` method for information regarding alignment of `T`. fn read_at(self, mut offset: u64) -> Result<&'a T> { self.read(&mut offset) } /// Get a reference to a slice of a `Pod` type at the given offset, and update the offset. /// /// Returns an error if offset or size are out of bounds. /// /// Also see the `read` method for information regarding alignment of `T`. fn read_slice(self, offset: &mut u64, count: usize) -> Result<&'a [T]> { let size = count .checked_mul(mem::size_of::()) .ok_or(())? .try_into() .map_err(|_| ())?; let bytes = self.read_bytes(offset, size)?; let (t, _) = slice_from_bytes(bytes, count)?; Ok(t) } /// Get a reference to a slice of a `Pod` type at the given offset. /// /// Returns an error if offset or size are out of bounds. /// /// Also see the `read` method for information regarding alignment of `T`. fn read_slice_at(self, mut offset: u64, count: usize) -> Result<&'a [T]> { self.read_slice(&mut offset, count) } } impl<'a> ReadRef<'a> for &'a [u8] { fn len(self) -> Result { self.len().try_into().map_err(|_| ()) } fn read_bytes_at(self, offset: u64, size: u64) -> Result<&'a [u8]> { let offset: usize = offset.try_into().map_err(|_| ())?; let size: usize = size.try_into().map_err(|_| ())?; self.get(offset..).ok_or(())?.get(..size).ok_or(()) } fn read_bytes_at_until(self, range: Range, delimiter: u8) -> Result<&'a [u8]> { let start: usize = range.start.try_into().map_err(|_| ())?; let end: usize = range.end.try_into().map_err(|_| ())?; let bytes = self.get(start..end).ok_or(())?; match memchr::memchr(delimiter, bytes) { Some(len) => { // This will never fail. bytes.get(..len).ok_or(()) } None => Err(()), } } } object-0.36.5/src/read/traits.rs000064400000000000000000000475201046102023000145530ustar 00000000000000use alloc::borrow::Cow; use alloc::vec::Vec; use crate::endian::Endianness; use crate::read::{ self, Architecture, CodeView, ComdatKind, CompressedData, CompressedFileRange, Export, FileFlags, Import, ObjectKind, ObjectMap, Relocation, RelocationMap, Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags, SubArchitecture, SymbolFlags, SymbolIndex, SymbolKind, SymbolMap, SymbolMapName, SymbolScope, SymbolSection, }; /// An object file. /// /// This is the primary trait for the unified read API. pub trait Object<'data>: read::private::Sealed { /// A loadable segment in the object file. type Segment<'file>: ObjectSegment<'data> where Self: 'file, 'data: 'file; /// An iterator for the loadable segments in the object file. type SegmentIterator<'file>: Iterator> where Self: 'file, 'data: 'file; /// A section in the object file. type Section<'file>: ObjectSection<'data> where Self: 'file, 'data: 'file; /// An iterator for the sections in the object file. type SectionIterator<'file>: Iterator> where Self: 'file, 'data: 'file; /// A COMDAT section group in the object file. type Comdat<'file>: ObjectComdat<'data> where Self: 'file, 'data: 'file; /// An iterator for the COMDAT section groups in the object file. type ComdatIterator<'file>: Iterator> where Self: 'file, 'data: 'file; /// A symbol in the object file. type Symbol<'file>: ObjectSymbol<'data> where Self: 'file, 'data: 'file; /// An iterator for symbols in the object file. type SymbolIterator<'file>: Iterator> where Self: 'file, 'data: 'file; /// A symbol table in the object file. type SymbolTable<'file>: ObjectSymbolTable< 'data, Symbol = Self::Symbol<'file>, SymbolIterator = Self::SymbolIterator<'file>, > where Self: 'file, 'data: 'file; /// An iterator for the dynamic relocations in the file. /// /// The first field in the item tuple is the address /// that the relocation applies to. type DynamicRelocationIterator<'file>: Iterator where Self: 'file, 'data: 'file; /// Get the architecture type of the file. fn architecture(&self) -> Architecture; /// Get the sub-architecture type of the file if known. /// /// A value of `None` has a range of meanings: the file supports all /// sub-architectures, the file does not explicitly specify a /// sub-architecture, or the sub-architecture is currently unrecognized. fn sub_architecture(&self) -> Option { None } /// Get the endianness of the file. #[inline] fn endianness(&self) -> Endianness { if self.is_little_endian() { Endianness::Little } else { Endianness::Big } } /// Return true if the file is little endian, false if it is big endian. fn is_little_endian(&self) -> bool; /// Return true if the file can contain 64-bit addresses. fn is_64(&self) -> bool; /// Return the kind of this object. fn kind(&self) -> ObjectKind; /// Get an iterator for the loadable segments in the file. /// /// For ELF, this is program headers with type [`PT_LOAD`](crate::elf::PT_LOAD). /// For Mach-O, this is load commands with type [`LC_SEGMENT`](crate::macho::LC_SEGMENT) /// or [`LC_SEGMENT_64`](crate::macho::LC_SEGMENT_64). /// For PE, this is all sections. fn segments(&self) -> Self::SegmentIterator<'_>; /// Get the section named `section_name`, if such a section exists. /// /// If `section_name` starts with a '.' then it is treated as a system /// section name, and is compared using the conventions specific to the /// object file format. This includes: /// - if ".debug_str_offsets" is requested for a Mach-O object file, then /// the actual section name that is searched for is "__debug_str_offs". /// - if ".debug_info" is requested for an ELF object file, then /// ".zdebug_info" may be returned (and similarly for other debug /// sections). Similarly, if ".debug_info" is requested for a Mach-O /// object file, then "__zdebug_info" may be returned. /// /// For some object files, multiple segments may contain sections with the /// same name. In this case, the first matching section will be used. /// /// This method skips over sections with invalid names. fn section_by_name(&self, section_name: &str) -> Option> { self.section_by_name_bytes(section_name.as_bytes()) } /// Like [`Self::section_by_name`], but allows names that are not UTF-8. fn section_by_name_bytes<'file>( &'file self, section_name: &[u8], ) -> Option>; /// Get the section at the given index. /// /// The meaning of the index depends on the object file. /// /// For some object files, this requires iterating through all sections. /// /// Returns an error if the index is invalid. fn section_by_index(&self, index: SectionIndex) -> Result>; /// Get an iterator for the sections in the file. fn sections(&self) -> Self::SectionIterator<'_>; /// Get an iterator for the COMDAT section groups in the file. fn comdats(&self) -> Self::ComdatIterator<'_>; /// Get the debugging symbol table, if any. fn symbol_table(&self) -> Option>; /// Get the debugging symbol at the given index. /// /// The meaning of the index depends on the object file. /// /// Returns an error if the index is invalid. fn symbol_by_index(&self, index: SymbolIndex) -> Result>; /// Get an iterator for the debugging symbols in the file. /// /// This may skip over symbols that are malformed or unsupported. /// /// For Mach-O files, this does not include STAB entries. fn symbols(&self) -> Self::SymbolIterator<'_>; /// Get the symbol named `symbol_name`, if the symbol exists. fn symbol_by_name<'file>(&'file self, symbol_name: &str) -> Option> { self.symbol_by_name_bytes(symbol_name.as_bytes()) } /// Like [`Self::symbol_by_name`], but allows names that are not UTF-8. fn symbol_by_name_bytes<'file>(&'file self, symbol_name: &[u8]) -> Option> { self.symbols() .find(|sym| sym.name_bytes() == Ok(symbol_name)) } /// Get the dynamic linking symbol table, if any. /// /// Only ELF has a separate dynamic linking symbol table. /// Consider using [`Self::exports`] or [`Self::imports`] instead. fn dynamic_symbol_table(&self) -> Option>; /// Get an iterator for the dynamic linking symbols in the file. /// /// This may skip over symbols that are malformed or unsupported. /// /// Only ELF has dynamic linking symbols. /// Other file formats will return an empty iterator. /// Consider using [`Self::exports`] or [`Self::imports`] instead. fn dynamic_symbols(&self) -> Self::SymbolIterator<'_>; /// Get the dynamic relocations for this file. /// /// Symbol indices in these relocations refer to the dynamic symbol table. /// /// Only ELF has dynamic relocations. fn dynamic_relocations(&self) -> Option>; /// Construct a map from addresses to symbol names. /// /// The map will only contain defined text and data symbols. /// The dynamic symbol table will only be used if there are no debugging symbols. fn symbol_map(&self) -> SymbolMap> { let mut symbols = Vec::new(); if let Some(table) = self.symbol_table().or_else(|| self.dynamic_symbol_table()) { // Sometimes symbols share addresses. Collect them all then choose the "best". let mut all_symbols = Vec::new(); for symbol in table.symbols() { // Must have an address. if !symbol.is_definition() { continue; } // Must have a name. let name = match symbol.name() { Ok(name) => name, _ => continue, }; if name.is_empty() { continue; } // Lower is better. let mut priority = 0u32; // Prefer known kind. match symbol.kind() { SymbolKind::Text | SymbolKind::Data => {} SymbolKind::Unknown => priority += 1, _ => continue, } priority *= 2; // Prefer global visibility. priority += match symbol.scope() { SymbolScope::Unknown => 3, SymbolScope::Compilation => 2, SymbolScope::Linkage => 1, SymbolScope::Dynamic => 0, }; priority *= 4; // Prefer later entries (earlier symbol is likely to be less specific). let index = !0 - symbol.index().0; // Tuple is ordered for sort. all_symbols.push((symbol.address(), priority, index, name)); } // Unstable sort is okay because tuple includes index. all_symbols.sort_unstable(); let mut previous_address = !0; for (address, _priority, _index, name) in all_symbols { if address != previous_address { symbols.push(SymbolMapName::new(address, name)); previous_address = address; } } } SymbolMap::new(symbols) } /// Construct a map from addresses to symbol names and object file names. /// /// This is derived from Mach-O STAB entries. fn object_map(&self) -> ObjectMap<'data> { ObjectMap::default() } /// Get the imported symbols. fn imports(&self) -> Result>>; /// Get the exported symbols that expose both a name and an address. /// /// Some file formats may provide other kinds of symbols that can be retrieved using /// the low level API. fn exports(&self) -> Result>>; /// Return true if the file contains DWARF debug information sections, false if not. fn has_debug_symbols(&self) -> bool; /// The UUID from a Mach-O [`LC_UUID`](crate::macho::LC_UUID) load command. #[inline] fn mach_uuid(&self) -> Result> { Ok(None) } /// The build ID from an ELF [`NT_GNU_BUILD_ID`](crate::elf::NT_GNU_BUILD_ID) note. #[inline] fn build_id(&self) -> Result> { Ok(None) } /// The filename and CRC from a `.gnu_debuglink` section. #[inline] fn gnu_debuglink(&self) -> Result> { Ok(None) } /// The filename and build ID from a `.gnu_debugaltlink` section. #[inline] fn gnu_debugaltlink(&self) -> Result> { Ok(None) } /// The filename and GUID from the PE CodeView section. #[inline] fn pdb_info(&self) -> Result>> { Ok(None) } /// Get the base address used for relative virtual addresses. /// /// Currently this is only non-zero for PE. fn relative_address_base(&self) -> u64; /// Get the virtual address of the entry point of the binary. fn entry(&self) -> u64; /// File flags that are specific to each file format. fn flags(&self) -> FileFlags; } /// A loadable segment in an [`Object`]. /// /// This trait is part of the unified read API. pub trait ObjectSegment<'data>: read::private::Sealed { /// Returns the virtual address of the segment. fn address(&self) -> u64; /// Returns the size of the segment in memory. fn size(&self) -> u64; /// Returns the alignment of the segment in memory. fn align(&self) -> u64; /// Returns the offset and size of the segment in the file. fn file_range(&self) -> (u64, u64); /// Returns a reference to the file contents of the segment. /// /// The length of this data may be different from the size of the /// segment in memory. fn data(&self) -> Result<&'data [u8]>; /// Return the segment data in the given range. /// /// Returns `Ok(None)` if the segment does not contain the given range. fn data_range(&self, address: u64, size: u64) -> Result>; /// Returns the name of the segment. fn name_bytes(&self) -> Result>; /// Returns the name of the segment. /// /// Returns an error if the name is not UTF-8. fn name(&self) -> Result>; /// Return the flags of segment. fn flags(&self) -> SegmentFlags; } /// A section in an [`Object`]. /// /// This trait is part of the unified read API. pub trait ObjectSection<'data>: read::private::Sealed { /// An iterator for the relocations for a section. /// /// The first field in the item tuple is the section offset /// that the relocation applies to. type RelocationIterator: Iterator; /// Returns the section index. fn index(&self) -> SectionIndex; /// Returns the address of the section. fn address(&self) -> u64; /// Returns the size of the section in memory. fn size(&self) -> u64; /// Returns the alignment of the section in memory. fn align(&self) -> u64; /// Returns offset and size of on-disk segment (if any). fn file_range(&self) -> Option<(u64, u64)>; /// Returns the raw contents of the section. /// /// The length of this data may be different from the size of the /// section in memory. /// /// This does not do any decompression. fn data(&self) -> Result<&'data [u8]>; /// Return the raw contents of the section data in the given range. /// /// This does not do any decompression. /// /// Returns `Ok(None)` if the section does not contain the given range. fn data_range(&self, address: u64, size: u64) -> Result>; /// Returns the potentially compressed file range of the section, /// along with information about the compression. fn compressed_file_range(&self) -> Result; /// Returns the potentially compressed contents of the section, /// along with information about the compression. fn compressed_data(&self) -> Result>; /// Returns the uncompressed contents of the section. /// /// The length of this data may be different from the size of the /// section in memory. /// /// If no compression is detected, then returns the data unchanged. /// Returns `Err` if decompression fails. fn uncompressed_data(&self) -> Result> { self.compressed_data()?.decompress() } /// Returns the name of the section. fn name_bytes(&self) -> Result<&'data [u8]>; /// Returns the name of the section. /// /// Returns an error if the name is not UTF-8. fn name(&self) -> Result<&'data str>; /// Returns the name of the segment for this section. fn segment_name_bytes(&self) -> Result>; /// Returns the name of the segment for this section. /// /// Returns an error if the name is not UTF-8. fn segment_name(&self) -> Result>; /// Return the kind of this section. fn kind(&self) -> SectionKind; /// Get the relocations for this section. fn relocations(&self) -> Self::RelocationIterator; /// Construct a relocation map for this section. fn relocation_map(&self) -> Result; /// Section flags that are specific to each file format. fn flags(&self) -> SectionFlags; } /// A COMDAT section group in an [`Object`]. /// /// This trait is part of the unified read API. pub trait ObjectComdat<'data>: read::private::Sealed { /// An iterator for the sections in the section group. type SectionIterator: Iterator; /// Returns the COMDAT selection kind. fn kind(&self) -> ComdatKind; /// Returns the index of the symbol used for the name of COMDAT section group. fn symbol(&self) -> SymbolIndex; /// Returns the name of the COMDAT section group. fn name_bytes(&self) -> Result<&'data [u8]>; /// Returns the name of the COMDAT section group. /// /// Returns an error if the name is not UTF-8. fn name(&self) -> Result<&'data str>; /// Get the sections in this section group. fn sections(&self) -> Self::SectionIterator; } /// A symbol table in an [`Object`]. /// /// This trait is part of the unified read API. pub trait ObjectSymbolTable<'data>: read::private::Sealed { /// A symbol table entry. type Symbol: ObjectSymbol<'data>; /// An iterator for the symbols in a symbol table. type SymbolIterator: Iterator; /// Get an iterator for the symbols in the table. /// /// This may skip over symbols that are malformed or unsupported. fn symbols(&self) -> Self::SymbolIterator; /// Get the symbol at the given index. /// /// The meaning of the index depends on the object file. /// /// Returns an error if the index is invalid. fn symbol_by_index(&self, index: SymbolIndex) -> Result; } /// A symbol table entry in an [`Object`]. /// /// This trait is part of the unified read API. pub trait ObjectSymbol<'data>: read::private::Sealed { /// The index of the symbol. fn index(&self) -> SymbolIndex; /// The name of the symbol. fn name_bytes(&self) -> Result<&'data [u8]>; /// The name of the symbol. /// /// Returns an error if the name is not UTF-8. fn name(&self) -> Result<&'data str>; /// The address of the symbol. May be zero if the address is unknown. fn address(&self) -> u64; /// The size of the symbol. May be zero if the size is unknown. fn size(&self) -> u64; /// Return the kind of this symbol. fn kind(&self) -> SymbolKind; /// Returns the section where the symbol is defined. fn section(&self) -> SymbolSection; /// Returns the section index for the section containing this symbol. /// /// May return `None` if the symbol is not defined in a section. fn section_index(&self) -> Option { self.section().index() } /// Return true if the symbol is undefined. fn is_undefined(&self) -> bool; /// Return true if the symbol is a definition of a function or data object /// that has a known address. /// /// This is primarily used to implement [`Object::symbol_map`]. fn is_definition(&self) -> bool; /// Return true if the symbol is common data. /// /// Note: does not check for [`SymbolSection::Section`] with [`SectionKind::Common`]. fn is_common(&self) -> bool; /// Return true if the symbol is weak. fn is_weak(&self) -> bool; /// Returns the symbol scope. fn scope(&self) -> SymbolScope; /// Return true if the symbol visible outside of the compilation unit. /// /// This treats [`SymbolScope::Unknown`] as global. fn is_global(&self) -> bool; /// Return true if the symbol is only visible within the compilation unit. fn is_local(&self) -> bool; /// Symbol flags that are specific to each file format. fn flags(&self) -> SymbolFlags; } /// An iterator for files that don't have dynamic relocations. #[derive(Debug)] pub struct NoDynamicRelocationIterator; impl Iterator for NoDynamicRelocationIterator { type Item = (u64, Relocation); #[inline] fn next(&mut self) -> Option { None } } object-0.36.5/src/read/util.rs000064400000000000000000000303751046102023000142220ustar 00000000000000use alloc::string::String; use core::convert::TryInto; use core::fmt; use core::marker::PhantomData; use crate::pod::{from_bytes, slice_from_bytes, Pod}; use crate::read::ReadRef; /// A newtype for byte slices. /// /// It has these important features: /// - no methods that can panic, such as `Index` /// - convenience methods for `Pod` types /// - a useful `Debug` implementation #[derive(Default, Clone, Copy, PartialEq, Eq)] pub struct Bytes<'data>(pub &'data [u8]); impl<'data> fmt::Debug for Bytes<'data> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { debug_list_bytes(self.0, fmt) } } impl<'data> Bytes<'data> { /// Return the length of the byte slice. #[inline] pub fn len(&self) -> usize { self.0.len() } /// Return true if the byte slice is empty. #[inline] pub fn is_empty(&self) -> bool { self.0.is_empty() } /// Skip over the given number of bytes at the start of the byte slice. /// /// Modifies the byte slice to start after the bytes. /// /// Returns an error if there are too few bytes. #[inline] pub fn skip(&mut self, offset: usize) -> Result<(), ()> { match self.0.get(offset..) { Some(tail) => { self.0 = tail; Ok(()) } None => { self.0 = &[]; Err(()) } } } /// Return a reference to the given number of bytes at the start of the byte slice. /// /// Modifies the byte slice to start after the bytes. /// /// Returns an error if there are too few bytes. #[inline] pub fn read_bytes(&mut self, count: usize) -> Result, ()> { match (self.0.get(..count), self.0.get(count..)) { (Some(head), Some(tail)) => { self.0 = tail; Ok(Bytes(head)) } _ => { self.0 = &[]; Err(()) } } } /// Return a reference to the given number of bytes at the given offset of the byte slice. /// /// Returns an error if the offset is invalid or there are too few bytes. #[inline] pub fn read_bytes_at(mut self, offset: usize, count: usize) -> Result, ()> { self.skip(offset)?; self.read_bytes(count) } /// Return a reference to a `Pod` struct at the start of the byte slice. /// /// Modifies the byte slice to start after the bytes. /// /// Returns an error if there are too few bytes or the slice is incorrectly aligned. #[inline] pub fn read(&mut self) -> Result<&'data T, ()> { match from_bytes(self.0) { Ok((value, tail)) => { self.0 = tail; Ok(value) } Err(()) => { self.0 = &[]; Err(()) } } } /// Return a reference to a `Pod` struct at the given offset of the byte slice. /// /// Returns an error if there are too few bytes or the offset is incorrectly aligned. #[inline] pub fn read_at(mut self, offset: usize) -> Result<&'data T, ()> { self.skip(offset)?; self.read() } /// Return a reference to a slice of `Pod` structs at the start of the byte slice. /// /// Modifies the byte slice to start after the bytes. /// /// Returns an error if there are too few bytes or the offset is incorrectly aligned. #[inline] pub fn read_slice(&mut self, count: usize) -> Result<&'data [T], ()> { match slice_from_bytes(self.0, count) { Ok((value, tail)) => { self.0 = tail; Ok(value) } Err(()) => { self.0 = &[]; Err(()) } } } /// Return a reference to a slice of `Pod` structs at the given offset of the byte slice. /// /// Returns an error if there are too few bytes or the offset is incorrectly aligned. #[inline] pub fn read_slice_at(mut self, offset: usize, count: usize) -> Result<&'data [T], ()> { self.skip(offset)?; self.read_slice(count) } /// Read a null terminated string. /// /// Does not assume any encoding. /// Reads past the null byte, but doesn't return it. #[inline] pub fn read_string(&mut self) -> Result<&'data [u8], ()> { match memchr::memchr(b'\0', self.0) { Some(null) => { // These will never fail. let bytes = self.read_bytes(null)?; self.skip(1)?; Ok(bytes.0) } None => { self.0 = &[]; Err(()) } } } /// Read a null terminated string at an offset. /// /// Does not assume any encoding. Does not return the null byte. #[inline] pub fn read_string_at(mut self, offset: usize) -> Result<&'data [u8], ()> { self.skip(offset)?; self.read_string() } /// Read an unsigned LEB128 number. pub fn read_uleb128(&mut self) -> Result { let mut result = 0; let mut shift = 0; loop { let byte = *self.read::()?; if shift == 63 && byte != 0x00 && byte != 0x01 { return Err(()); } result |= u64::from(byte & 0x7f) << shift; shift += 7; if byte & 0x80 == 0 { return Ok(result); } } } /// Read a signed LEB128 number. pub fn read_sleb128(&mut self) -> Result { let mut result = 0; let mut shift = 0; loop { let byte = *self.read::()?; if shift == 63 && byte != 0x00 && byte != 0x7f { return Err(()); } result |= i64::from(byte & 0x7f) << shift; shift += 7; if byte & 0x80 == 0 { if shift < 64 && (byte & 0x40) != 0 { // Sign extend the result. result |= !0 << shift; } return Ok(result); } } } } // Only for Debug impl of `Bytes`. fn debug_list_bytes(bytes: &[u8], fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = fmt.debug_list(); list.entries(bytes.iter().take(8).copied().map(DebugByte)); if bytes.len() > 8 { list.entry(&DebugLen(bytes.len())); } list.finish() } struct DebugByte(u8); impl fmt::Debug for DebugByte { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!(fmt, "0x{:02x}", self.0) } } struct DebugLen(usize); impl fmt::Debug for DebugLen { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!(fmt, "...; {}", self.0) } } /// A newtype for byte strings. /// /// For byte slices that are strings of an unknown encoding. /// /// Provides a `Debug` implementation that interprets the bytes as UTF-8. #[derive(Default, Clone, Copy, PartialEq, Eq)] pub(crate) struct ByteString<'data>(pub &'data [u8]); impl<'data> fmt::Debug for ByteString<'data> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!(fmt, "\"{}\"", String::from_utf8_lossy(self.0)) } } #[allow(dead_code)] #[inline] pub(crate) fn align(offset: usize, size: usize) -> usize { (offset + (size - 1)) & !(size - 1) } #[allow(dead_code)] pub(crate) fn data_range( data: &[u8], data_address: u64, range_address: u64, size: u64, ) -> Option<&[u8]> { let offset = range_address.checked_sub(data_address)?; data.get(offset.try_into().ok()?..)? .get(..size.try_into().ok()?) } /// A table of zero-terminated strings. /// /// This is used by most file formats for strings such as section names and symbol names. #[derive(Debug, Clone, Copy)] pub struct StringTable<'data, R = &'data [u8]> where R: ReadRef<'data>, { data: Option, start: u64, end: u64, marker: PhantomData<&'data ()>, } impl<'data, R: ReadRef<'data>> StringTable<'data, R> { /// Interpret the given data as a string table. pub fn new(data: R, start: u64, end: u64) -> Self { StringTable { data: Some(data), start, end, marker: PhantomData, } } /// Return the string at the given offset. pub fn get(&self, offset: u32) -> Result<&'data [u8], ()> { match self.data { Some(data) => { let r_start = self.start.checked_add(offset.into()).ok_or(())?; data.read_bytes_at_until(r_start..self.end, 0) } None => Err(()), } } } impl<'data, R: ReadRef<'data>> Default for StringTable<'data, R> { fn default() -> Self { StringTable { data: None, start: 0, end: 0, marker: PhantomData, } } } #[cfg(test)] mod tests { use super::*; use crate::pod::bytes_of; #[test] fn bytes() { let x = u32::to_be(0x0123_4567); let data = Bytes(bytes_of(&x)); let mut bytes = data; assert_eq!(bytes.skip(0), Ok(())); assert_eq!(bytes, data); let mut bytes = data; assert_eq!(bytes.skip(4), Ok(())); assert_eq!(bytes, Bytes(&[])); let mut bytes = data; assert_eq!(bytes.skip(5), Err(())); assert_eq!(bytes, Bytes(&[])); let mut bytes = data; assert_eq!(bytes.read_bytes(0), Ok(Bytes(&[]))); assert_eq!(bytes, data); let mut bytes = data; assert_eq!(bytes.read_bytes(4), Ok(data)); assert_eq!(bytes, Bytes(&[])); let mut bytes = data; assert_eq!(bytes.read_bytes(5), Err(())); assert_eq!(bytes, Bytes(&[])); assert_eq!(data.read_bytes_at(0, 0), Ok(Bytes(&[]))); assert_eq!(data.read_bytes_at(4, 0), Ok(Bytes(&[]))); assert_eq!(data.read_bytes_at(0, 4), Ok(data)); assert_eq!(data.read_bytes_at(1, 4), Err(())); let mut bytes = data; assert_eq!(bytes.read::(), Ok(&u16::to_be(0x0123))); assert_eq!(bytes, Bytes(&[0x45, 0x67])); assert_eq!(data.read_at::(2), Ok(&u16::to_be(0x4567))); assert_eq!(data.read_at::(3), Err(())); assert_eq!(data.read_at::(4), Err(())); let mut bytes = data; assert_eq!(bytes.read::(), Ok(&x)); assert_eq!(bytes, Bytes(&[])); let mut bytes = data; assert_eq!(bytes.read::(), Err(())); assert_eq!(bytes, Bytes(&[])); let mut bytes = data; assert_eq!(bytes.read_slice::(0), Ok(&[][..])); assert_eq!(bytes, data); let mut bytes = data; assert_eq!(bytes.read_slice::(4), Ok(data.0)); assert_eq!(bytes, Bytes(&[])); let mut bytes = data; assert_eq!(bytes.read_slice::(5), Err(())); assert_eq!(bytes, Bytes(&[])); assert_eq!(data.read_slice_at::(0, 0), Ok(&[][..])); assert_eq!(data.read_slice_at::(4, 0), Ok(&[][..])); assert_eq!(data.read_slice_at::(0, 4), Ok(data.0)); assert_eq!(data.read_slice_at::(1, 4), Err(())); let data = Bytes(&[0x01, 0x02, 0x00, 0x04]); let mut bytes = data; assert_eq!(bytes.read_string(), Ok(&data.0[..2])); assert_eq!(bytes.0, &data.0[3..]); let mut bytes = data; bytes.skip(3).unwrap(); assert_eq!(bytes.read_string(), Err(())); assert_eq!(bytes.0, &[]); assert_eq!(data.read_string_at(0), Ok(&data.0[..2])); assert_eq!(data.read_string_at(1), Ok(&data.0[1..2])); assert_eq!(data.read_string_at(2), Ok(&[][..])); assert_eq!(data.read_string_at(3), Err(())); } #[test] fn bytes_debug() { assert_eq!(format!("{:?}", Bytes(&[])), "[]"); assert_eq!(format!("{:?}", Bytes(&[0x01])), "[0x01]"); assert_eq!( format!( "{:?}", Bytes(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]) ), "[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]" ); assert_eq!( format!( "{:?}", Bytes(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09]) ), "[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, ...; 9]" ); } } object-0.36.5/src/read/wasm.rs000064400000000000000000000777051046102023000142240ustar 00000000000000//! Support for reading Wasm files. //! //! [`WasmFile`] implements the [`Object`] trait for Wasm files. use alloc::boxed::Box; use alloc::vec::Vec; use core::marker::PhantomData; use core::ops::Range; use core::{slice, str}; use wasmparser as wp; use crate::read::{ self, Architecture, ComdatKind, CompressedData, CompressedFileRange, Error, Export, FileFlags, Import, NoDynamicRelocationIterator, Object, ObjectComdat, ObjectKind, ObjectSection, ObjectSegment, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, Relocation, RelocationMap, Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags, SymbolFlags, SymbolIndex, SymbolKind, SymbolScope, SymbolSection, }; #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(usize)] enum SectionId { Custom = 0, Type = 1, Import = 2, Function = 3, Table = 4, Memory = 5, Global = 6, Export = 7, Start = 8, Element = 9, Code = 10, Data = 11, DataCount = 12, Tag = 13, } // Update this constant when adding new section id: const MAX_SECTION_ID: usize = SectionId::Tag as usize; /// A WebAssembly object file. #[derive(Debug)] pub struct WasmFile<'data, R = &'data [u8]> { data: &'data [u8], has_memory64: bool, // All sections, including custom sections. sections: Vec>, // Indices into `sections` of sections with a non-zero id. id_sections: Box<[Option; MAX_SECTION_ID + 1]>, // Whether the file has DWARF information. has_debug_symbols: bool, // Symbols collected from imports, exports, code and name sections. symbols: Vec>, // Address of the function body for the entry point. entry: u64, marker: PhantomData, } #[derive(Debug)] struct SectionHeader<'data> { id: SectionId, range: Range, name: &'data str, } #[derive(Clone)] enum LocalFunctionKind { Unknown, Exported { symbol_ids: Vec }, Local { symbol_id: u32 }, } impl ReadError for wasmparser::Result { fn read_error(self, error: &'static str) -> Result { self.map_err(|_| Error(error)) } } impl<'data, R: ReadRef<'data>> WasmFile<'data, R> { /// Parse the raw wasm data. pub fn parse(data: R) -> Result { let len = data.len().read_error("Unknown Wasm file size")?; let data = data.read_bytes_at(0, len).read_error("Wasm read failed")?; let parser = wp::Parser::new(0).parse_all(data); let mut file = WasmFile { data, has_memory64: false, sections: Vec::new(), id_sections: Default::default(), has_debug_symbols: false, symbols: Vec::new(), entry: 0, marker: PhantomData, }; let mut main_file_symbol = Some(WasmSymbolInternal { name: "", address: 0, size: 0, kind: SymbolKind::File, section: SymbolSection::None, scope: SymbolScope::Compilation, }); let mut imported_funcs_count = 0; let mut local_func_kinds = Vec::new(); let mut entry_func_id = None; let mut code_range_start = 0; let mut code_func_index = 0; // One-to-one mapping of globals to their value (if the global is a constant integer). let mut global_values = Vec::new(); for payload in parser { let payload = payload.read_error("Invalid Wasm section header")?; match payload { wp::Payload::Version { encoding, .. } => { if encoding != wp::Encoding::Module { return Err(Error("Unsupported Wasm encoding")); } } wp::Payload::TypeSection(section) => { file.add_section(SectionId::Type, section.range(), ""); } wp::Payload::ImportSection(section) => { file.add_section(SectionId::Import, section.range(), ""); let mut last_module_name = None; for import in section { let import = import.read_error("Couldn't read an import item")?; let module_name = import.module; if last_module_name != Some(module_name) { file.symbols.push(WasmSymbolInternal { name: module_name, address: 0, size: 0, kind: SymbolKind::File, section: SymbolSection::None, scope: SymbolScope::Dynamic, }); last_module_name = Some(module_name); } let kind = match import.ty { wp::TypeRef::Func(_) => { imported_funcs_count += 1; SymbolKind::Text } wp::TypeRef::Memory(memory) => { file.has_memory64 |= memory.memory64; SymbolKind::Data } wp::TypeRef::Table(_) | wp::TypeRef::Global(_) => SymbolKind::Data, wp::TypeRef::Tag(_) => SymbolKind::Unknown, }; file.symbols.push(WasmSymbolInternal { name: import.name, address: 0, size: 0, kind, section: SymbolSection::Undefined, scope: SymbolScope::Dynamic, }); } } wp::Payload::FunctionSection(section) => { file.add_section(SectionId::Function, section.range(), ""); local_func_kinds = vec![LocalFunctionKind::Unknown; section.into_iter().count()]; } wp::Payload::TableSection(section) => { file.add_section(SectionId::Table, section.range(), ""); } wp::Payload::MemorySection(section) => { file.add_section(SectionId::Memory, section.range(), ""); for memory in section { let memory = memory.read_error("Couldn't read a memory item")?; file.has_memory64 |= memory.memory64; } } wp::Payload::GlobalSection(section) => { file.add_section(SectionId::Global, section.range(), ""); for global in section { let global = global.read_error("Couldn't read a global item")?; let mut address = None; if !global.ty.mutable { // There should be exactly one instruction. let init = global.init_expr.get_operators_reader().read(); address = match init.read_error("Couldn't read a global init expr")? { wp::Operator::I32Const { value } => Some(value as u64), wp::Operator::I64Const { value } => Some(value as u64), _ => None, }; } global_values.push(address); } } wp::Payload::ExportSection(section) => { file.add_section(SectionId::Export, section.range(), ""); if let Some(main_file_symbol) = main_file_symbol.take() { file.symbols.push(main_file_symbol); } for export in section { let export = export.read_error("Couldn't read an export item")?; let (kind, section_idx) = match export.kind { wp::ExternalKind::Func => { if let Some(local_func_id) = export.index.checked_sub(imported_funcs_count) { let local_func_kind = local_func_kinds .get_mut(local_func_id as usize) .read_error("Invalid Wasm export index")?; if let LocalFunctionKind::Unknown = local_func_kind { *local_func_kind = LocalFunctionKind::Exported { symbol_ids: Vec::new(), }; } let symbol_ids = match local_func_kind { LocalFunctionKind::Exported { symbol_ids } => symbol_ids, _ => unreachable!(), }; symbol_ids.push(file.symbols.len() as u32); } (SymbolKind::Text, SectionId::Code) } wp::ExternalKind::Table | wp::ExternalKind::Memory | wp::ExternalKind::Global => (SymbolKind::Data, SectionId::Data), // TODO wp::ExternalKind::Tag => continue, }; // Try to guess the symbol address. Rust and C export a global containing // the address in linear memory of the symbol. let mut address = 0; if export.kind == wp::ExternalKind::Global { if let Some(&Some(x)) = global_values.get(export.index as usize) { address = x; } } file.symbols.push(WasmSymbolInternal { name: export.name, address, size: 0, kind, section: SymbolSection::Section(SectionIndex(section_idx as usize)), scope: SymbolScope::Dynamic, }); } } wp::Payload::StartSection { func, range, .. } => { file.add_section(SectionId::Start, range, ""); entry_func_id = Some(func); } wp::Payload::ElementSection(section) => { file.add_section(SectionId::Element, section.range(), ""); } wp::Payload::CodeSectionStart { range, .. } => { code_range_start = range.start; file.add_section(SectionId::Code, range, ""); if let Some(main_file_symbol) = main_file_symbol.take() { file.symbols.push(main_file_symbol); } } wp::Payload::CodeSectionEntry(body) => { let i = code_func_index; code_func_index += 1; let range = body.range(); let address = range.start as u64 - code_range_start as u64; let size = (range.end - range.start) as u64; if entry_func_id == Some(i as u32) { file.entry = address; } let local_func_kind = local_func_kinds .get_mut(i) .read_error("Invalid Wasm code section index")?; match local_func_kind { LocalFunctionKind::Unknown => { *local_func_kind = LocalFunctionKind::Local { symbol_id: file.symbols.len() as u32, }; file.symbols.push(WasmSymbolInternal { name: "", address, size, kind: SymbolKind::Text, section: SymbolSection::Section(SectionIndex( SectionId::Code as usize, )), scope: SymbolScope::Compilation, }); } LocalFunctionKind::Exported { symbol_ids } => { for symbol_id in core::mem::take(symbol_ids) { let export_symbol = &mut file.symbols[symbol_id as usize]; export_symbol.address = address; export_symbol.size = size; } } _ => unreachable!(), } } wp::Payload::DataSection(section) => { file.add_section(SectionId::Data, section.range(), ""); } wp::Payload::DataCountSection { range, .. } => { file.add_section(SectionId::DataCount, range, ""); } wp::Payload::TagSection(section) => { file.add_section(SectionId::Tag, section.range(), ""); } wp::Payload::CustomSection(section) => { let name = section.name(); let size = section.data().len(); let mut range = section.range(); range.start = range.end - size; file.add_section(SectionId::Custom, range, name); if name == "name" { let reader = wp::BinaryReader::new(section.data(), section.data_offset()); for name in wp::NameSectionReader::new(reader) { // TODO: Right now, ill-formed name subsections // are silently ignored in order to maintain // compatibility with extended name sections, which // are not yet supported by the version of // `wasmparser` currently used. // A better fix would be to update `wasmparser` to // the newest version, but this requires // a major rewrite of this file. if let Ok(wp::Name::Function(name_map)) = name { for naming in name_map { let naming = naming.read_error("Couldn't read a function name")?; if let Some(local_index) = naming.index.checked_sub(imported_funcs_count) { if let LocalFunctionKind::Local { symbol_id } = local_func_kinds[local_index as usize] { file.symbols[symbol_id as usize].name = naming.name; } } } } } } else if name.starts_with(".debug_") { file.has_debug_symbols = true; } } _ => {} } } Ok(file) } fn add_section(&mut self, id: SectionId, range: Range, name: &'data str) { let section = SectionHeader { id, range, name }; self.id_sections[id as usize] = Some(self.sections.len()); self.sections.push(section); } } impl<'data, R> read::private::Sealed for WasmFile<'data, R> {} impl<'data, R: ReadRef<'data>> Object<'data> for WasmFile<'data, R> { type Segment<'file> = WasmSegment<'data, 'file, R> where Self: 'file, 'data: 'file; type SegmentIterator<'file> = WasmSegmentIterator<'data, 'file, R> where Self: 'file, 'data: 'file; type Section<'file> = WasmSection<'data, 'file, R> where Self: 'file, 'data: 'file; type SectionIterator<'file> = WasmSectionIterator<'data, 'file, R> where Self: 'file, 'data: 'file; type Comdat<'file> = WasmComdat<'data, 'file, R> where Self: 'file, 'data: 'file; type ComdatIterator<'file> = WasmComdatIterator<'data, 'file, R> where Self: 'file, 'data: 'file; type Symbol<'file> = WasmSymbol<'data, 'file> where Self: 'file, 'data: 'file; type SymbolIterator<'file> = WasmSymbolIterator<'data, 'file> where Self: 'file, 'data: 'file; type SymbolTable<'file> = WasmSymbolTable<'data, 'file> where Self: 'file, 'data: 'file; type DynamicRelocationIterator<'file> = NoDynamicRelocationIterator where Self: 'file, 'data: 'file; #[inline] fn architecture(&self) -> Architecture { if self.has_memory64 { Architecture::Wasm64 } else { Architecture::Wasm32 } } #[inline] fn is_little_endian(&self) -> bool { true } #[inline] fn is_64(&self) -> bool { self.has_memory64 } fn kind(&self) -> ObjectKind { // TODO: check for `linking` custom section ObjectKind::Unknown } fn segments(&self) -> Self::SegmentIterator<'_> { WasmSegmentIterator { file: self } } fn section_by_name_bytes<'file>( &'file self, section_name: &[u8], ) -> Option> { self.sections() .find(|section| section.name_bytes() == Ok(section_name)) } fn section_by_index(&self, index: SectionIndex) -> Result> { // TODO: Missing sections should return an empty section. let id_section = self .id_sections .get(index.0) .and_then(|x| *x) .read_error("Invalid Wasm section index")?; let section = self.sections.get(id_section).unwrap(); Ok(WasmSection { file: self, section, }) } fn sections(&self) -> Self::SectionIterator<'_> { WasmSectionIterator { file: self, sections: self.sections.iter(), } } fn comdats(&self) -> Self::ComdatIterator<'_> { WasmComdatIterator { file: self } } #[inline] fn symbol_by_index(&self, index: SymbolIndex) -> Result> { let symbol = self .symbols .get(index.0) .read_error("Invalid Wasm symbol index")?; Ok(WasmSymbol { index, symbol }) } fn symbols(&self) -> Self::SymbolIterator<'_> { WasmSymbolIterator { symbols: self.symbols.iter().enumerate(), } } fn symbol_table(&self) -> Option> { Some(WasmSymbolTable { symbols: &self.symbols, }) } fn dynamic_symbols(&self) -> Self::SymbolIterator<'_> { WasmSymbolIterator { symbols: [].iter().enumerate(), } } #[inline] fn dynamic_symbol_table(&self) -> Option> { None } #[inline] fn dynamic_relocations(&self) -> Option { None } fn imports(&self) -> Result>> { // TODO: return entries in the import section Ok(Vec::new()) } fn exports(&self) -> Result>> { // TODO: return entries in the export section Ok(Vec::new()) } fn has_debug_symbols(&self) -> bool { self.has_debug_symbols } fn relative_address_base(&self) -> u64 { 0 } #[inline] fn entry(&self) -> u64 { self.entry } #[inline] fn flags(&self) -> FileFlags { FileFlags::None } } /// An iterator for the segments in a [`WasmFile`]. /// /// This is a stub that doesn't implement any functionality. #[derive(Debug)] pub struct WasmSegmentIterator<'data, 'file, R = &'data [u8]> { #[allow(unused)] file: &'file WasmFile<'data, R>, } impl<'data, 'file, R> Iterator for WasmSegmentIterator<'data, 'file, R> { type Item = WasmSegment<'data, 'file, R>; #[inline] fn next(&mut self) -> Option { None } } /// A segment in a [`WasmFile`]. /// /// This is a stub that doesn't implement any functionality. #[derive(Debug)] pub struct WasmSegment<'data, 'file, R = &'data [u8]> { #[allow(unused)] file: &'file WasmFile<'data, R>, } impl<'data, 'file, R> read::private::Sealed for WasmSegment<'data, 'file, R> {} impl<'data, 'file, R> ObjectSegment<'data> for WasmSegment<'data, 'file, R> { #[inline] fn address(&self) -> u64 { unreachable!() } #[inline] fn size(&self) -> u64 { unreachable!() } #[inline] fn align(&self) -> u64 { unreachable!() } #[inline] fn file_range(&self) -> (u64, u64) { unreachable!() } fn data(&self) -> Result<&'data [u8]> { unreachable!() } fn data_range(&self, _address: u64, _size: u64) -> Result> { unreachable!() } #[inline] fn name_bytes(&self) -> Result> { unreachable!() } #[inline] fn name(&self) -> Result> { unreachable!() } #[inline] fn flags(&self) -> SegmentFlags { unreachable!() } } /// An iterator for the sections in a [`WasmFile`]. #[derive(Debug)] pub struct WasmSectionIterator<'data, 'file, R = &'data [u8]> { file: &'file WasmFile<'data, R>, sections: slice::Iter<'file, SectionHeader<'data>>, } impl<'data, 'file, R> Iterator for WasmSectionIterator<'data, 'file, R> { type Item = WasmSection<'data, 'file, R>; fn next(&mut self) -> Option { let section = self.sections.next()?; Some(WasmSection { file: self.file, section, }) } } /// A section in a [`WasmFile`]. /// /// Most functionality is provided by the [`ObjectSection`] trait implementation. #[derive(Debug)] pub struct WasmSection<'data, 'file, R = &'data [u8]> { file: &'file WasmFile<'data, R>, section: &'file SectionHeader<'data>, } impl<'data, 'file, R> read::private::Sealed for WasmSection<'data, 'file, R> {} impl<'data, 'file, R: ReadRef<'data>> ObjectSection<'data> for WasmSection<'data, 'file, R> { type RelocationIterator = WasmRelocationIterator<'data, 'file, R>; #[inline] fn index(&self) -> SectionIndex { // Note that we treat all custom sections as index 0. // This is ok because they are never looked up by index. SectionIndex(self.section.id as usize) } #[inline] fn address(&self) -> u64 { 0 } #[inline] fn size(&self) -> u64 { let range = &self.section.range; (range.end - range.start) as u64 } #[inline] fn align(&self) -> u64 { 1 } #[inline] fn file_range(&self) -> Option<(u64, u64)> { let range = &self.section.range; Some((range.start as _, range.end as _)) } #[inline] fn data(&self) -> Result<&'data [u8]> { let range = &self.section.range; self.file .data .read_bytes_at(range.start as u64, range.end as u64 - range.start as u64) .read_error("Invalid Wasm section size or offset") } fn data_range(&self, _address: u64, _size: u64) -> Result> { unimplemented!() } #[inline] fn compressed_file_range(&self) -> Result { Ok(CompressedFileRange::none(self.file_range())) } #[inline] fn compressed_data(&self) -> Result> { self.data().map(CompressedData::none) } #[inline] fn name_bytes(&self) -> Result<&'data [u8]> { self.name().map(str::as_bytes) } #[inline] fn name(&self) -> Result<&'data str> { Ok(match self.section.id { SectionId::Custom => self.section.name, SectionId::Type => "", SectionId::Import => "", SectionId::Function => "", SectionId::Table => "", SectionId::Memory => "", SectionId::Global => "", SectionId::Export => "", SectionId::Start => "", SectionId::Element => "", SectionId::Code => "", SectionId::Data => "", SectionId::DataCount => "", SectionId::Tag => "", }) } #[inline] fn segment_name_bytes(&self) -> Result> { Ok(None) } #[inline] fn segment_name(&self) -> Result> { Ok(None) } #[inline] fn kind(&self) -> SectionKind { match self.section.id { SectionId::Custom => match self.section.name { "reloc." | "linking" => SectionKind::Linker, _ => SectionKind::Other, }, SectionId::Type => SectionKind::Metadata, SectionId::Import => SectionKind::Linker, SectionId::Function => SectionKind::Metadata, SectionId::Table => SectionKind::UninitializedData, SectionId::Memory => SectionKind::UninitializedData, SectionId::Global => SectionKind::Data, SectionId::Export => SectionKind::Linker, SectionId::Start => SectionKind::Linker, SectionId::Element => SectionKind::Data, SectionId::Code => SectionKind::Text, SectionId::Data => SectionKind::Data, SectionId::DataCount => SectionKind::UninitializedData, SectionId::Tag => SectionKind::Data, } } #[inline] fn relocations(&self) -> WasmRelocationIterator<'data, 'file, R> { WasmRelocationIterator(PhantomData) } fn relocation_map(&self) -> read::Result { RelocationMap::new(self.file, self) } #[inline] fn flags(&self) -> SectionFlags { SectionFlags::None } } /// An iterator for the COMDAT section groups in a [`WasmFile`]. /// /// This is a stub that doesn't implement any functionality. #[derive(Debug)] pub struct WasmComdatIterator<'data, 'file, R = &'data [u8]> { #[allow(unused)] file: &'file WasmFile<'data, R>, } impl<'data, 'file, R> Iterator for WasmComdatIterator<'data, 'file, R> { type Item = WasmComdat<'data, 'file, R>; #[inline] fn next(&mut self) -> Option { None } } /// A COMDAT section group in a [`WasmFile`]. /// /// This is a stub that doesn't implement any functionality. #[derive(Debug)] pub struct WasmComdat<'data, 'file, R = &'data [u8]> { #[allow(unused)] file: &'file WasmFile<'data, R>, } impl<'data, 'file, R> read::private::Sealed for WasmComdat<'data, 'file, R> {} impl<'data, 'file, R> ObjectComdat<'data> for WasmComdat<'data, 'file, R> { type SectionIterator = WasmComdatSectionIterator<'data, 'file, R>; #[inline] fn kind(&self) -> ComdatKind { unreachable!(); } #[inline] fn symbol(&self) -> SymbolIndex { unreachable!(); } #[inline] fn name_bytes(&self) -> Result<&'data [u8]> { unreachable!(); } #[inline] fn name(&self) -> Result<&'data str> { unreachable!(); } #[inline] fn sections(&self) -> Self::SectionIterator { unreachable!(); } } /// An iterator for the sections in a COMDAT section group in a [`WasmFile`]. /// /// This is a stub that doesn't implement any functionality. #[derive(Debug)] pub struct WasmComdatSectionIterator<'data, 'file, R = &'data [u8]> { #[allow(unused)] file: &'file WasmFile<'data, R>, } impl<'data, 'file, R> Iterator for WasmComdatSectionIterator<'data, 'file, R> { type Item = SectionIndex; fn next(&mut self) -> Option { None } } /// A symbol table in a [`WasmFile`]. #[derive(Debug)] pub struct WasmSymbolTable<'data, 'file> { symbols: &'file [WasmSymbolInternal<'data>], } impl<'data, 'file> read::private::Sealed for WasmSymbolTable<'data, 'file> {} impl<'data, 'file> ObjectSymbolTable<'data> for WasmSymbolTable<'data, 'file> { type Symbol = WasmSymbol<'data, 'file>; type SymbolIterator = WasmSymbolIterator<'data, 'file>; fn symbols(&self) -> Self::SymbolIterator { WasmSymbolIterator { symbols: self.symbols.iter().enumerate(), } } fn symbol_by_index(&self, index: SymbolIndex) -> Result { let symbol = self .symbols .get(index.0) .read_error("Invalid Wasm symbol index")?; Ok(WasmSymbol { index, symbol }) } } /// An iterator for the symbols in a [`WasmFile`]. #[derive(Debug)] pub struct WasmSymbolIterator<'data, 'file> { symbols: core::iter::Enumerate>>, } impl<'data, 'file> Iterator for WasmSymbolIterator<'data, 'file> { type Item = WasmSymbol<'data, 'file>; fn next(&mut self) -> Option { let (index, symbol) = self.symbols.next()?; Some(WasmSymbol { index: SymbolIndex(index), symbol, }) } } /// A symbol in a [`WasmFile`]. /// /// Most functionality is provided by the [`ObjectSymbol`] trait implementation. #[derive(Clone, Copy, Debug)] pub struct WasmSymbol<'data, 'file> { index: SymbolIndex, symbol: &'file WasmSymbolInternal<'data>, } #[derive(Clone, Debug)] struct WasmSymbolInternal<'data> { name: &'data str, address: u64, size: u64, kind: SymbolKind, section: SymbolSection, scope: SymbolScope, } impl<'data, 'file> read::private::Sealed for WasmSymbol<'data, 'file> {} impl<'data, 'file> ObjectSymbol<'data> for WasmSymbol<'data, 'file> { #[inline] fn index(&self) -> SymbolIndex { self.index } #[inline] fn name_bytes(&self) -> read::Result<&'data [u8]> { Ok(self.symbol.name.as_bytes()) } #[inline] fn name(&self) -> read::Result<&'data str> { Ok(self.symbol.name) } #[inline] fn address(&self) -> u64 { self.symbol.address } #[inline] fn size(&self) -> u64 { self.symbol.size } #[inline] fn kind(&self) -> SymbolKind { self.symbol.kind } #[inline] fn section(&self) -> SymbolSection { self.symbol.section } #[inline] fn is_undefined(&self) -> bool { self.symbol.section == SymbolSection::Undefined } #[inline] fn is_definition(&self) -> bool { (self.symbol.kind == SymbolKind::Text || self.symbol.kind == SymbolKind::Data) && self.symbol.section != SymbolSection::Undefined } #[inline] fn is_common(&self) -> bool { self.symbol.section == SymbolSection::Common } #[inline] fn is_weak(&self) -> bool { false } #[inline] fn scope(&self) -> SymbolScope { self.symbol.scope } #[inline] fn is_global(&self) -> bool { self.symbol.scope != SymbolScope::Compilation } #[inline] fn is_local(&self) -> bool { self.symbol.scope == SymbolScope::Compilation } #[inline] fn flags(&self) -> SymbolFlags { SymbolFlags::None } } /// An iterator for the relocations for a [`WasmSection`]. /// /// This is a stub that doesn't implement any functionality. #[derive(Debug)] pub struct WasmRelocationIterator<'data, 'file, R = &'data [u8]>( PhantomData<(&'data (), &'file (), R)>, ); impl<'data, 'file, R> Iterator for WasmRelocationIterator<'data, 'file, R> { type Item = (u64, Relocation); #[inline] fn next(&mut self) -> Option { None } } object-0.36.5/src/read/xcoff/comdat.rs000064400000000000000000000074241046102023000156200ustar 00000000000000//! XCOFF doesn't support the COMDAT section. use core::fmt::Debug; use crate::read::{self, ComdatKind, ObjectComdat, ReadRef, Result, SectionIndex, SymbolIndex}; use crate::xcoff; use super::{FileHeader, XcoffFile}; /// An iterator for the COMDAT section groups in a [`XcoffFile32`](super::XcoffFile32). pub type XcoffComdatIterator32<'data, 'file, R = &'data [u8]> = XcoffComdatIterator<'data, 'file, xcoff::FileHeader32, R>; /// An iterator for the COMDAT section groups in a [`XcoffFile64`](super::XcoffFile64). pub type XcoffComdatIterator64<'data, 'file, R = &'data [u8]> = XcoffComdatIterator<'data, 'file, xcoff::FileHeader64, R>; /// An iterator for the COMDAT section groups in a [`XcoffFile`]. /// /// This is a stub that doesn't implement any functionality. #[derive(Debug)] pub struct XcoffComdatIterator<'data, 'file, Xcoff, R = &'data [u8]> where Xcoff: FileHeader, R: ReadRef<'data>, { #[allow(unused)] pub(crate) file: &'file XcoffFile<'data, Xcoff, R>, } impl<'data, 'file, Xcoff, R> Iterator for XcoffComdatIterator<'data, 'file, Xcoff, R> where Xcoff: FileHeader, R: ReadRef<'data>, { type Item = XcoffComdat<'data, 'file, Xcoff, R>; #[inline] fn next(&mut self) -> Option { None } } /// A COMDAT section group in a [`XcoffFile32`](super::XcoffFile32). pub type XcoffComdat32<'data, 'file, R = &'data [u8]> = XcoffComdat<'data, 'file, xcoff::FileHeader32, R>; /// A COMDAT section group in a [`XcoffFile64`](super::XcoffFile64). pub type XcoffComdat64<'data, 'file, R = &'data [u8]> = XcoffComdat<'data, 'file, xcoff::FileHeader64, R>; /// A COMDAT section group in a [`XcoffFile`]. /// /// This is a stub that doesn't implement any functionality. #[derive(Debug)] pub struct XcoffComdat<'data, 'file, Xcoff, R = &'data [u8]> where Xcoff: FileHeader, R: ReadRef<'data>, { #[allow(unused)] file: &'file XcoffFile<'data, Xcoff, R>, } impl<'data, 'file, Xcoff, R> read::private::Sealed for XcoffComdat<'data, 'file, Xcoff, R> where Xcoff: FileHeader, R: ReadRef<'data>, { } impl<'data, 'file, Xcoff, R> ObjectComdat<'data> for XcoffComdat<'data, 'file, Xcoff, R> where Xcoff: FileHeader, R: ReadRef<'data>, { type SectionIterator = XcoffComdatSectionIterator<'data, 'file, Xcoff, R>; #[inline] fn kind(&self) -> ComdatKind { unreachable!(); } #[inline] fn symbol(&self) -> SymbolIndex { unreachable!(); } #[inline] fn name_bytes(&self) -> Result<&'data [u8]> { unreachable!(); } #[inline] fn name(&self) -> Result<&'data str> { unreachable!(); } #[inline] fn sections(&self) -> Self::SectionIterator { unreachable!(); } } /// An iterator for the sections in a COMDAT section group in a [`XcoffFile32`](super::XcoffFile32). pub type XcoffComdatSectionIterator32<'data, 'file, R = &'data [u8]> = XcoffComdatSectionIterator<'data, 'file, xcoff::FileHeader32, R>; /// An iterator for the sections in a COMDAT section group in a [`XcoffFile64`](super::XcoffFile64). pub type XcoffComdatSectionIterator64<'data, 'file, R = &'data [u8]> = XcoffComdatSectionIterator<'data, 'file, xcoff::FileHeader64, R>; /// An iterator for the sections in a COMDAT section group in a [`XcoffFile`]. /// /// This is a stub that doesn't implement any functionality. #[derive(Debug)] pub struct XcoffComdatSectionIterator<'data, 'file, Xcoff, R = &'data [u8]> where Xcoff: FileHeader, R: ReadRef<'data>, { #[allow(unused)] file: &'file XcoffFile<'data, Xcoff, R>, } impl<'data, 'file, Xcoff, R> Iterator for XcoffComdatSectionIterator<'data, 'file, Xcoff, R> where Xcoff: FileHeader, R: ReadRef<'data>, { type Item = SectionIndex; fn next(&mut self) -> Option { None } } object-0.36.5/src/read/xcoff/file.rs000064400000000000000000000433351046102023000152710ustar 00000000000000use core::fmt::Debug; use core::mem; use alloc::vec::Vec; use crate::endian::BigEndian as BE; use crate::pod::Pod; use crate::read::{ self, Architecture, Error, Export, FileFlags, Import, NoDynamicRelocationIterator, Object, ObjectKind, ObjectSection, ReadError, ReadRef, Result, SectionIndex, SymbolIndex, }; use crate::xcoff; use super::{ CsectAux, FileAux, Rel, SectionHeader, SectionTable, Symbol, SymbolTable, XcoffComdat, XcoffComdatIterator, XcoffSection, XcoffSectionIterator, XcoffSegment, XcoffSegmentIterator, XcoffSymbol, XcoffSymbolIterator, XcoffSymbolTable, }; /// A 32-bit XCOFF object file. /// /// This is a file that starts with [`xcoff::FileHeader32`], and corresponds /// to [`crate::FileKind::Xcoff32`]. pub type XcoffFile32<'data, R = &'data [u8]> = XcoffFile<'data, xcoff::FileHeader32, R>; /// A 64-bit XCOFF object file. /// /// This is a file that starts with [`xcoff::FileHeader64`], and corresponds /// to [`crate::FileKind::Xcoff64`]. pub type XcoffFile64<'data, R = &'data [u8]> = XcoffFile<'data, xcoff::FileHeader64, R>; /// A partially parsed XCOFF file. /// /// Most functionality is provided by the [`Object`] trait implementation. #[derive(Debug)] pub struct XcoffFile<'data, Xcoff, R = &'data [u8]> where Xcoff: FileHeader, R: ReadRef<'data>, { pub(super) data: R, pub(super) header: &'data Xcoff, pub(super) aux_header: Option<&'data Xcoff::AuxHeader>, pub(super) sections: SectionTable<'data, Xcoff>, pub(super) symbols: SymbolTable<'data, Xcoff, R>, } impl<'data, Xcoff, R> XcoffFile<'data, Xcoff, R> where Xcoff: FileHeader, R: ReadRef<'data>, { /// Parse the raw XCOFF file data. pub fn parse(data: R) -> Result { let mut offset = 0; let header = Xcoff::parse(data, &mut offset)?; let aux_header = header.aux_header(data, &mut offset)?; let sections = header.sections(data, &mut offset)?; let symbols = header.symbols(data)?; Ok(XcoffFile { data, header, aux_header, sections, symbols, }) } /// Returns the raw data. pub fn data(&self) -> R { self.data } /// Returns the raw XCOFF file header. #[deprecated(note = "Use `xcoff_header` instead")] pub fn raw_header(&self) -> &'data Xcoff { self.header } /// Get the raw XCOFF file header. pub fn xcoff_header(&self) -> &'data Xcoff { self.header } /// Get the raw XCOFF auxiliary header. pub fn xcoff_aux_header(&self) -> Option<&'data Xcoff::AuxHeader> { self.aux_header } /// Get the XCOFF section table. pub fn xcoff_section_table(&self) -> &SectionTable<'data, Xcoff> { &self.sections } /// Get the XCOFF symbol table. pub fn xcoff_symbol_table(&self) -> &SymbolTable<'data, Xcoff, R> { &self.symbols } } impl<'data, Xcoff, R> read::private::Sealed for XcoffFile<'data, Xcoff, R> where Xcoff: FileHeader, R: ReadRef<'data>, { } impl<'data, Xcoff, R> Object<'data> for XcoffFile<'data, Xcoff, R> where Xcoff: FileHeader, R: ReadRef<'data>, { type Segment<'file> = XcoffSegment<'data, 'file, Xcoff, R> where Self: 'file, 'data: 'file; type SegmentIterator<'file> = XcoffSegmentIterator<'data, 'file, Xcoff, R> where Self: 'file, 'data: 'file; type Section<'file> = XcoffSection<'data, 'file, Xcoff, R> where Self: 'file, 'data: 'file; type SectionIterator<'file> = XcoffSectionIterator<'data, 'file, Xcoff, R> where Self: 'file, 'data: 'file; type Comdat<'file> = XcoffComdat<'data, 'file, Xcoff, R> where Self: 'file, 'data: 'file; type ComdatIterator<'file> = XcoffComdatIterator<'data, 'file, Xcoff, R> where Self: 'file, 'data: 'file; type Symbol<'file> = XcoffSymbol<'data, 'file, Xcoff, R> where Self: 'file, 'data: 'file; type SymbolIterator<'file> = XcoffSymbolIterator<'data, 'file, Xcoff, R> where Self: 'file, 'data: 'file; type SymbolTable<'file> = XcoffSymbolTable<'data, 'file, Xcoff, R> where Self: 'file, 'data: 'file; type DynamicRelocationIterator<'file> = NoDynamicRelocationIterator where Self: 'file, 'data: 'file; fn architecture(&self) -> Architecture { if self.is_64() { Architecture::PowerPc64 } else { Architecture::PowerPc } } fn is_little_endian(&self) -> bool { false } fn is_64(&self) -> bool { self.header.is_type_64() } fn kind(&self) -> ObjectKind { let flags = self.header.f_flags(); if flags & xcoff::F_EXEC != 0 { ObjectKind::Executable } else if flags & xcoff::F_SHROBJ != 0 { ObjectKind::Dynamic } else if flags & xcoff::F_RELFLG == 0 { ObjectKind::Relocatable } else { ObjectKind::Unknown } } fn segments(&self) -> XcoffSegmentIterator<'data, '_, Xcoff, R> { XcoffSegmentIterator { file: self } } fn section_by_name_bytes<'file>( &'file self, section_name: &[u8], ) -> Option> { self.sections() .find(|section| section.name_bytes() == Ok(section_name)) } fn section_by_index(&self, index: SectionIndex) -> Result> { let section = self.sections.section(index)?; Ok(XcoffSection { file: self, section, index, }) } fn sections(&self) -> XcoffSectionIterator<'data, '_, Xcoff, R> { XcoffSectionIterator { file: self, iter: self.sections.iter().enumerate(), } } fn comdats(&self) -> XcoffComdatIterator<'data, '_, Xcoff, R> { XcoffComdatIterator { file: self } } fn symbol_table(&self) -> Option> { if self.symbols.is_empty() { return None; } Some(XcoffSymbolTable { symbols: &self.symbols, file: self, }) } fn symbol_by_index(&self, index: SymbolIndex) -> Result> { let symbol = self.symbols.symbol(index)?; Ok(XcoffSymbol { symbols: &self.symbols, index, symbol, file: self, }) } fn symbols(&self) -> XcoffSymbolIterator<'data, '_, Xcoff, R> { XcoffSymbolIterator { file: self, symbols: self.symbols.iter(), } } fn dynamic_symbol_table<'file>( &'file self, ) -> Option> { None } fn dynamic_symbols(&self) -> XcoffSymbolIterator<'data, '_, Xcoff, R> { // TODO: return the symbols in the STYP_LOADER section. XcoffSymbolIterator { file: self, symbols: self.symbols.iter_none(), } } fn dynamic_relocations(&self) -> Option> { // TODO: return the relocations in the STYP_LOADER section. None } fn imports(&self) -> Result>> { // TODO: return the imports in the STYP_LOADER section. Ok(Vec::new()) } fn exports(&self) -> Result>> { // TODO: return the exports in the STYP_LOADER section. Ok(Vec::new()) } fn has_debug_symbols(&self) -> bool { self.section_by_name(".debug").is_some() || self.section_by_name(".dwinfo").is_some() } fn relative_address_base(&self) -> u64 { 0 } fn entry(&self) -> u64 { if let Some(aux_header) = self.aux_header { aux_header.o_entry().into() } else { 0 } } fn flags(&self) -> FileFlags { FileFlags::Xcoff { f_flags: self.header.f_flags(), } } } /// A trait for generic access to [`xcoff::FileHeader32`] and [`xcoff::FileHeader64`]. #[allow(missing_docs)] pub trait FileHeader: Debug + Pod { type Word: Into; type AuxHeader: AuxHeader; type SectionHeader: SectionHeader; type Symbol: Symbol; type FileAux: FileAux; type CsectAux: CsectAux; type Rel: Rel; /// Return true if this type is a 64-bit header. fn is_type_64(&self) -> bool; fn f_magic(&self) -> u16; fn f_nscns(&self) -> u16; fn f_timdat(&self) -> u32; fn f_symptr(&self) -> Self::Word; fn f_nsyms(&self) -> u32; fn f_opthdr(&self) -> u16; fn f_flags(&self) -> u16; // Provided methods. /// Read the file header. /// /// Also checks that the magic field in the file header is a supported format. fn parse<'data, R: ReadRef<'data>>(data: R, offset: &mut u64) -> Result<&'data Self> { let header = data .read::(offset) .read_error("Invalid XCOFF header size or alignment")?; if !header.is_supported() { return Err(Error("Unsupported XCOFF header")); } Ok(header) } fn is_supported(&self) -> bool { (self.is_type_64() && self.f_magic() == xcoff::MAGIC_64) || (!self.is_type_64() && self.f_magic() == xcoff::MAGIC_32) } /// Read the auxiliary file header. fn aux_header<'data, R: ReadRef<'data>>( &self, data: R, offset: &mut u64, ) -> Result> { let aux_header_size = self.f_opthdr(); if self.f_flags() & xcoff::F_EXEC == 0 { // No auxiliary header is required for an object file that is not an executable. // TODO: Some AIX programs generate auxiliary headers for 32-bit object files // that end after the data_start field. *offset += u64::from(aux_header_size); return Ok(None); } // Executables, however, must have auxiliary headers that include the // full structure definitions. if aux_header_size != mem::size_of::() as u16 { *offset += u64::from(aux_header_size); return Ok(None); } let aux_header = data .read::(offset) .read_error("Invalid XCOFF auxiliary header size")?; Ok(Some(aux_header)) } /// Read the section table. #[inline] fn sections<'data, R: ReadRef<'data>>( &self, data: R, offset: &mut u64, ) -> Result> { SectionTable::parse(self, data, offset) } /// Return the symbol table. #[inline] fn symbols<'data, R: ReadRef<'data>>(&self, data: R) -> Result> { SymbolTable::parse(*self, data) } } impl FileHeader for xcoff::FileHeader32 { type Word = u32; type AuxHeader = xcoff::AuxHeader32; type SectionHeader = xcoff::SectionHeader32; type Symbol = xcoff::Symbol32; type FileAux = xcoff::FileAux32; type CsectAux = xcoff::CsectAux32; type Rel = xcoff::Rel32; fn is_type_64(&self) -> bool { false } fn f_magic(&self) -> u16 { self.f_magic.get(BE) } fn f_nscns(&self) -> u16 { self.f_nscns.get(BE) } fn f_timdat(&self) -> u32 { self.f_timdat.get(BE) } fn f_symptr(&self) -> Self::Word { self.f_symptr.get(BE) } fn f_nsyms(&self) -> u32 { self.f_nsyms.get(BE) } fn f_opthdr(&self) -> u16 { self.f_opthdr.get(BE) } fn f_flags(&self) -> u16 { self.f_flags.get(BE) } } impl FileHeader for xcoff::FileHeader64 { type Word = u64; type AuxHeader = xcoff::AuxHeader64; type SectionHeader = xcoff::SectionHeader64; type Symbol = xcoff::Symbol64; type FileAux = xcoff::FileAux64; type CsectAux = xcoff::CsectAux64; type Rel = xcoff::Rel64; fn is_type_64(&self) -> bool { true } fn f_magic(&self) -> u16 { self.f_magic.get(BE) } fn f_nscns(&self) -> u16 { self.f_nscns.get(BE) } fn f_timdat(&self) -> u32 { self.f_timdat.get(BE) } fn f_symptr(&self) -> Self::Word { self.f_symptr.get(BE) } fn f_nsyms(&self) -> u32 { self.f_nsyms.get(BE) } fn f_opthdr(&self) -> u16 { self.f_opthdr.get(BE) } fn f_flags(&self) -> u16 { self.f_flags.get(BE) } } /// A trait for generic access to [`xcoff::AuxHeader32`] and [`xcoff::AuxHeader64`]. #[allow(missing_docs)] pub trait AuxHeader: Debug + Pod { type Word: Into; fn o_mflag(&self) -> u16; fn o_vstamp(&self) -> u16; fn o_tsize(&self) -> Self::Word; fn o_dsize(&self) -> Self::Word; fn o_bsize(&self) -> Self::Word; fn o_entry(&self) -> Self::Word; fn o_text_start(&self) -> Self::Word; fn o_data_start(&self) -> Self::Word; fn o_toc(&self) -> Self::Word; fn o_snentry(&self) -> u16; fn o_sntext(&self) -> u16; fn o_sndata(&self) -> u16; fn o_sntoc(&self) -> u16; fn o_snloader(&self) -> u16; fn o_snbss(&self) -> u16; fn o_algntext(&self) -> u16; fn o_algndata(&self) -> u16; fn o_modtype(&self) -> u16; fn o_cpuflag(&self) -> u8; fn o_cputype(&self) -> u8; fn o_maxstack(&self) -> Self::Word; fn o_maxdata(&self) -> Self::Word; fn o_debugger(&self) -> u32; fn o_textpsize(&self) -> u8; fn o_datapsize(&self) -> u8; fn o_stackpsize(&self) -> u8; fn o_flags(&self) -> u8; fn o_sntdata(&self) -> u16; fn o_sntbss(&self) -> u16; fn o_x64flags(&self) -> Option; } impl AuxHeader for xcoff::AuxHeader32 { type Word = u32; fn o_mflag(&self) -> u16 { self.o_mflag.get(BE) } fn o_vstamp(&self) -> u16 { self.o_vstamp.get(BE) } fn o_tsize(&self) -> Self::Word { self.o_tsize.get(BE) } fn o_dsize(&self) -> Self::Word { self.o_dsize.get(BE) } fn o_bsize(&self) -> Self::Word { self.o_bsize.get(BE) } fn o_entry(&self) -> Self::Word { self.o_entry.get(BE) } fn o_text_start(&self) -> Self::Word { self.o_text_start.get(BE) } fn o_data_start(&self) -> Self::Word { self.o_data_start.get(BE) } fn o_toc(&self) -> Self::Word { self.o_toc.get(BE) } fn o_snentry(&self) -> u16 { self.o_snentry.get(BE) } fn o_sntext(&self) -> u16 { self.o_sntext.get(BE) } fn o_sndata(&self) -> u16 { self.o_sndata.get(BE) } fn o_sntoc(&self) -> u16 { self.o_sntoc.get(BE) } fn o_snloader(&self) -> u16 { self.o_snloader.get(BE) } fn o_snbss(&self) -> u16 { self.o_snbss.get(BE) } fn o_algntext(&self) -> u16 { self.o_algntext.get(BE) } fn o_algndata(&self) -> u16 { self.o_algndata.get(BE) } fn o_modtype(&self) -> u16 { self.o_modtype.get(BE) } fn o_cpuflag(&self) -> u8 { self.o_cpuflag } fn o_cputype(&self) -> u8 { self.o_cputype } fn o_maxstack(&self) -> Self::Word { self.o_maxstack.get(BE) } fn o_maxdata(&self) -> Self::Word { self.o_maxdata.get(BE) } fn o_debugger(&self) -> u32 { self.o_debugger.get(BE) } fn o_textpsize(&self) -> u8 { self.o_textpsize } fn o_datapsize(&self) -> u8 { self.o_datapsize } fn o_stackpsize(&self) -> u8 { self.o_stackpsize } fn o_flags(&self) -> u8 { self.o_flags } fn o_sntdata(&self) -> u16 { self.o_sntdata.get(BE) } fn o_sntbss(&self) -> u16 { self.o_sntbss.get(BE) } fn o_x64flags(&self) -> Option { None } } impl AuxHeader for xcoff::AuxHeader64 { type Word = u64; fn o_mflag(&self) -> u16 { self.o_mflag.get(BE) } fn o_vstamp(&self) -> u16 { self.o_vstamp.get(BE) } fn o_tsize(&self) -> Self::Word { self.o_tsize.get(BE) } fn o_dsize(&self) -> Self::Word { self.o_dsize.get(BE) } fn o_bsize(&self) -> Self::Word { self.o_bsize.get(BE) } fn o_entry(&self) -> Self::Word { self.o_entry.get(BE) } fn o_text_start(&self) -> Self::Word { self.o_text_start.get(BE) } fn o_data_start(&self) -> Self::Word { self.o_data_start.get(BE) } fn o_toc(&self) -> Self::Word { self.o_toc.get(BE) } fn o_snentry(&self) -> u16 { self.o_snentry.get(BE) } fn o_sntext(&self) -> u16 { self.o_sntext.get(BE) } fn o_sndata(&self) -> u16 { self.o_sndata.get(BE) } fn o_sntoc(&self) -> u16 { self.o_sntoc.get(BE) } fn o_snloader(&self) -> u16 { self.o_snloader.get(BE) } fn o_snbss(&self) -> u16 { self.o_snbss.get(BE) } fn o_algntext(&self) -> u16 { self.o_algntext.get(BE) } fn o_algndata(&self) -> u16 { self.o_algndata.get(BE) } fn o_modtype(&self) -> u16 { self.o_modtype.get(BE) } fn o_cpuflag(&self) -> u8 { self.o_cpuflag } fn o_cputype(&self) -> u8 { self.o_cputype } fn o_maxstack(&self) -> Self::Word { self.o_maxstack.get(BE) } fn o_maxdata(&self) -> Self::Word { self.o_maxdata.get(BE) } fn o_debugger(&self) -> u32 { self.o_debugger.get(BE) } fn o_textpsize(&self) -> u8 { self.o_textpsize } fn o_datapsize(&self) -> u8 { self.o_datapsize } fn o_stackpsize(&self) -> u8 { self.o_stackpsize } fn o_flags(&self) -> u8 { self.o_flags } fn o_sntdata(&self) -> u16 { self.o_sntdata.get(BE) } fn o_sntbss(&self) -> u16 { self.o_sntbss.get(BE) } fn o_x64flags(&self) -> Option { Some(self.o_x64flags.get(BE)) } } object-0.36.5/src/read/xcoff/mod.rs000064400000000000000000000034541046102023000151270ustar 00000000000000//! Support for reading AIX XCOFF files. //! //! Traits are used to abstract over the difference between 32-bit and 64-bit XCOFF. //! The primary trait for this is [`FileHeader`]. //! //! ## High level API //! //! [`XcoffFile`] implements the [`Object`](crate::read::Object) trait for XCOFF files. //! [`XcoffFile`] is parameterised by [`FileHeader`] to allow reading both 32-bit and //! 64-bit XCOFF. There are type aliases for these parameters ([`XcoffFile32`] and //! [`XcoffFile64`]). //! //! ## Low level API //! //! The [`FileHeader`] trait can be directly used to parse both [`xcoff::FileHeader32`] //! and [`xcoff::FileHeader64`]. //! //! ### Example for low level API //! ```no_run //! use object::xcoff; //! use object::read::xcoff::{FileHeader, SectionHeader, Symbol}; //! use std::error::Error; //! use std::fs; //! //! /// Reads a file and displays the name of each section and symbol. //! fn main() -> Result<(), Box> { //! # #[cfg(feature = "std")] { //! let data = fs::read("path/to/binary")?; //! let mut offset = 0; //! let header = xcoff::FileHeader64::parse(&*data, &mut offset)?; //! let aux_header = header.aux_header(&*data, &mut offset)?; //! let sections = header.sections(&*data, &mut offset)?; //! let symbols = header.symbols(&*data)?; //! for section in sections.iter() { //! println!("{}", String::from_utf8_lossy(section.name())); //! } //! for (_index, symbol) in symbols.iter() { //! println!("{}", String::from_utf8_lossy(symbol.name(symbols.strings())?)); //! } //! # } //! Ok(()) //! } //! ``` #[cfg(doc)] use crate::xcoff; mod file; pub use file::*; mod section; pub use section::*; mod symbol; pub use symbol::*; mod relocation; pub use relocation::*; mod comdat; pub use comdat::*; mod segment; pub use segment::*; object-0.36.5/src/read/xcoff/relocation.rs000064400000000000000000000075121046102023000165060ustar 00000000000000use alloc::fmt; use core::fmt::Debug; use core::slice; use crate::endian::BigEndian as BE; use crate::pod::Pod; use crate::read::{ ReadRef, Relocation, RelocationEncoding, RelocationFlags, RelocationKind, RelocationTarget, SymbolIndex, }; use crate::xcoff; use super::{FileHeader, SectionHeader, XcoffFile}; /// An iterator for the relocations in an [`XcoffSection32`](super::XcoffSection32). pub type XcoffRelocationIterator32<'data, 'file, R = &'data [u8]> = XcoffRelocationIterator<'data, 'file, xcoff::FileHeader32, R>; /// An iterator for the relocations in an [`XcoffSection64`](super::XcoffSection64). pub type XcoffRelocationIterator64<'data, 'file, R = &'data [u8]> = XcoffRelocationIterator<'data, 'file, xcoff::FileHeader64, R>; /// An iterator for the relocations in an [`XcoffSection`](super::XcoffSection). pub struct XcoffRelocationIterator<'data, 'file, Xcoff, R = &'data [u8]> where Xcoff: FileHeader, R: ReadRef<'data>, { #[allow(unused)] pub(super) file: &'file XcoffFile<'data, Xcoff, R>, pub(super) relocations: slice::Iter<'data, <::SectionHeader as SectionHeader>::Rel>, } impl<'data, 'file, Xcoff, R> Iterator for XcoffRelocationIterator<'data, 'file, Xcoff, R> where Xcoff: FileHeader, R: ReadRef<'data>, { type Item = (u64, Relocation); fn next(&mut self) -> Option { self.relocations.next().map(|relocation| { let r_rtype = relocation.r_rtype(); let r_rsize = relocation.r_rsize(); let flags = RelocationFlags::Xcoff { r_rtype, r_rsize }; let encoding = RelocationEncoding::Generic; let (kind, addend) = match r_rtype { xcoff::R_POS | xcoff::R_RL | xcoff::R_RLA | xcoff::R_BA | xcoff::R_RBA | xcoff::R_TLS => (RelocationKind::Absolute, 0), xcoff::R_REL | xcoff::R_BR | xcoff::R_RBR => (RelocationKind::Relative, -4), xcoff::R_TOC | xcoff::R_TOCL | xcoff::R_TOCU => (RelocationKind::Got, 0), _ => (RelocationKind::Unknown, 0), }; let size = (r_rsize & 0x3F) + 1; let target = RelocationTarget::Symbol(relocation.symbol()); ( relocation.r_vaddr().into(), Relocation { kind, encoding, size, target, addend, implicit_addend: true, flags, }, ) }) } } impl<'data, 'file, Xcoff, R> fmt::Debug for XcoffRelocationIterator<'data, 'file, Xcoff, R> where Xcoff: FileHeader, R: ReadRef<'data>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("XcoffRelocationIterator").finish() } } /// A trait for generic access to [`xcoff::Rel32`] and [`xcoff::Rel64`]. #[allow(missing_docs)] pub trait Rel: Debug + Pod { type Word: Into; fn r_vaddr(&self) -> Self::Word; fn r_symndx(&self) -> u32; fn r_rsize(&self) -> u8; fn r_rtype(&self) -> u8; fn symbol(&self) -> SymbolIndex { SymbolIndex(self.r_symndx() as usize) } } impl Rel for xcoff::Rel32 { type Word = u32; fn r_vaddr(&self) -> Self::Word { self.r_vaddr.get(BE) } fn r_symndx(&self) -> u32 { self.r_symndx.get(BE) } fn r_rsize(&self) -> u8 { self.r_rsize } fn r_rtype(&self) -> u8 { self.r_rtype } } impl Rel for xcoff::Rel64 { type Word = u64; fn r_vaddr(&self) -> Self::Word { self.r_vaddr.get(BE) } fn r_symndx(&self) -> u32 { self.r_symndx.get(BE) } fn r_rsize(&self) -> u8 { self.r_rsize } fn r_rtype(&self) -> u8 { self.r_rtype } } object-0.36.5/src/read/xcoff/section.rs000064400000000000000000000313221046102023000160070ustar 00000000000000use core::fmt::Debug; use core::{iter, result, slice, str}; use crate::endian::BigEndian as BE; use crate::pod::Pod; use crate::read::{ self, CompressedData, CompressedFileRange, Error, ObjectSection, ReadError, ReadRef, RelocationMap, Result, SectionFlags, SectionIndex, SectionKind, }; use crate::xcoff; use super::{AuxHeader, FileHeader, Rel, XcoffFile, XcoffRelocationIterator}; /// An iterator for the sections in an [`XcoffFile32`](super::XcoffFile32). pub type XcoffSectionIterator32<'data, 'file, R = &'data [u8]> = XcoffSectionIterator<'data, 'file, xcoff::FileHeader32, R>; /// An iterator for the sections in an [`XcoffFile64`](super::XcoffFile64). pub type XcoffSectionIterator64<'data, 'file, R = &'data [u8]> = XcoffSectionIterator<'data, 'file, xcoff::FileHeader64, R>; /// An iterator for the sections in an [`XcoffFile`]. #[derive(Debug)] pub struct XcoffSectionIterator<'data, 'file, Xcoff, R = &'data [u8]> where Xcoff: FileHeader, R: ReadRef<'data>, { pub(super) file: &'file XcoffFile<'data, Xcoff, R>, pub(super) iter: iter::Enumerate>, } impl<'data, 'file, Xcoff, R> Iterator for XcoffSectionIterator<'data, 'file, Xcoff, R> where Xcoff: FileHeader, R: ReadRef<'data>, { type Item = XcoffSection<'data, 'file, Xcoff, R>; fn next(&mut self) -> Option { self.iter.next().map(|(index, section)| XcoffSection { index: SectionIndex(index + 1), file: self.file, section, }) } } /// A section in an [`XcoffFile32`](super::XcoffFile32). pub type XcoffSection32<'data, 'file, R = &'data [u8]> = XcoffSection<'data, 'file, xcoff::FileHeader32, R>; /// A section in an [`XcoffFile64`](super::XcoffFile64). pub type XcoffSection64<'data, 'file, R = &'data [u8]> = XcoffSection<'data, 'file, xcoff::FileHeader64, R>; /// A section in an [`XcoffFile`]. /// /// Most functionality is provided by the [`ObjectSection`] trait implementation. #[derive(Debug)] pub struct XcoffSection<'data, 'file, Xcoff, R = &'data [u8]> where Xcoff: FileHeader, R: ReadRef<'data>, { pub(super) file: &'file XcoffFile<'data, Xcoff, R>, pub(super) section: &'data Xcoff::SectionHeader, pub(super) index: SectionIndex, } impl<'data, 'file, Xcoff: FileHeader, R: ReadRef<'data>> XcoffSection<'data, 'file, Xcoff, R> { /// Get the XCOFF file containing this section. pub fn xcoff_file(&self) -> &'file XcoffFile<'data, Xcoff, R> { self.file } /// Get the raw XCOFF section header. pub fn xcoff_section(&self) -> &'data Xcoff::SectionHeader { self.section } /// Get the raw XCOFF relocation entries for this section. pub fn xcoff_relocations(&self) -> Result<&'data [Xcoff::Rel]> { self.section.relocations(self.file.data) } fn bytes(&self) -> Result<&'data [u8]> { self.section .data(self.file.data) .read_error("Invalid XCOFF section offset or size") } } impl<'data, 'file, Xcoff, R> read::private::Sealed for XcoffSection<'data, 'file, Xcoff, R> where Xcoff: FileHeader, R: ReadRef<'data>, { } impl<'data, 'file, Xcoff, R> ObjectSection<'data> for XcoffSection<'data, 'file, Xcoff, R> where Xcoff: FileHeader, R: ReadRef<'data>, { type RelocationIterator = XcoffRelocationIterator<'data, 'file, Xcoff, R>; fn index(&self) -> SectionIndex { self.index } fn address(&self) -> u64 { self.section.s_paddr().into() } fn size(&self) -> u64 { self.section.s_size().into() } fn align(&self) -> u64 { // The default section alignment is 4. if let Some(aux_header) = self.file.aux_header { match self.kind() { SectionKind::Text => aux_header.o_algntext().into(), SectionKind::Data => aux_header.o_algndata().into(), _ => 4, } } else { 4 } } fn file_range(&self) -> Option<(u64, u64)> { self.section.file_range() } fn data(&self) -> Result<&'data [u8]> { self.bytes() } fn data_range(&self, address: u64, size: u64) -> Result> { Ok(read::util::data_range( self.bytes()?, self.address(), address, size, )) } fn compressed_file_range(&self) -> Result { Ok(CompressedFileRange::none(self.file_range())) } fn compressed_data(&self) -> Result> { self.data().map(CompressedData::none) } fn name_bytes(&self) -> read::Result<&'data [u8]> { Ok(self.section.name()) } fn name(&self) -> read::Result<&'data str> { let name = self.name_bytes()?; str::from_utf8(name) .ok() .read_error("Non UTF-8 XCOFF section name") } fn segment_name_bytes(&self) -> Result> { Ok(None) } fn segment_name(&self) -> Result> { Ok(None) } fn kind(&self) -> SectionKind { let section_type = self.section.s_flags() as u16; if section_type & xcoff::STYP_TEXT != 0 { SectionKind::Text } else if section_type & xcoff::STYP_DATA != 0 { SectionKind::Data } else if section_type & xcoff::STYP_TDATA != 0 { SectionKind::Tls } else if section_type & xcoff::STYP_BSS != 0 { SectionKind::UninitializedData } else if section_type & xcoff::STYP_TBSS != 0 { SectionKind::UninitializedTls } else if section_type & (xcoff::STYP_DEBUG | xcoff::STYP_DWARF) != 0 { SectionKind::Debug } else if section_type & (xcoff::STYP_LOADER | xcoff::STYP_OVRFLO) != 0 { SectionKind::Metadata } else if section_type & (xcoff::STYP_INFO | xcoff::STYP_EXCEPT | xcoff::STYP_PAD | xcoff::STYP_TYPCHK) != 0 { SectionKind::Other } else { SectionKind::Unknown } } fn relocations(&self) -> Self::RelocationIterator { let rel = self.xcoff_relocations().unwrap_or(&[]); XcoffRelocationIterator { file: self.file, relocations: rel.iter(), } } fn relocation_map(&self) -> read::Result { RelocationMap::new(self.file, self) } fn flags(&self) -> SectionFlags { SectionFlags::Xcoff { s_flags: self.section.s_flags(), } } fn uncompressed_data(&self) -> Result> { self.compressed_data()?.decompress() } } /// The table of section headers in an XCOFF file. /// /// Returned by [`FileHeader::sections`]. #[derive(Debug, Clone, Copy)] pub struct SectionTable<'data, Xcoff: FileHeader> { sections: &'data [Xcoff::SectionHeader], } impl<'data, Xcoff> Default for SectionTable<'data, Xcoff> where Xcoff: FileHeader, { fn default() -> Self { Self { sections: &[] } } } impl<'data, Xcoff> SectionTable<'data, Xcoff> where Xcoff: FileHeader, { /// Parse the section table. /// /// `data` must be the entire file data. /// `offset` must be after the optional file header. pub fn parse>(header: &Xcoff, data: R, offset: &mut u64) -> Result { let section_num = header.f_nscns(); if section_num == 0 { return Ok(SectionTable::default()); } let sections = data .read_slice(offset, section_num as usize) .read_error("Invalid XCOFF section headers")?; Ok(SectionTable { sections }) } /// Iterate over the section headers. #[inline] pub fn iter(&self) -> slice::Iter<'data, Xcoff::SectionHeader> { self.sections.iter() } /// Return true if the section table is empty. #[inline] pub fn is_empty(&self) -> bool { self.sections.is_empty() } /// The number of section headers. #[inline] pub fn len(&self) -> usize { self.sections.len() } /// Return the section header at the given index. /// /// The index is 1-based. pub fn section(&self, index: SectionIndex) -> read::Result<&'data Xcoff::SectionHeader> { self.sections .get(index.0.wrapping_sub(1)) .read_error("Invalid XCOFF section index") } } /// A trait for generic access to [`xcoff::SectionHeader32`] and [`xcoff::SectionHeader64`]. #[allow(missing_docs)] pub trait SectionHeader: Debug + Pod { type Word: Into; type HalfWord: Into; type Xcoff: FileHeader; type Rel: Rel; fn s_name(&self) -> &[u8; 8]; fn s_paddr(&self) -> Self::Word; fn s_vaddr(&self) -> Self::Word; fn s_size(&self) -> Self::Word; fn s_scnptr(&self) -> Self::Word; fn s_relptr(&self) -> Self::Word; fn s_lnnoptr(&self) -> Self::Word; fn s_nreloc(&self) -> Self::HalfWord; fn s_nlnno(&self) -> Self::HalfWord; fn s_flags(&self) -> u32; /// Return the section name. fn name(&self) -> &[u8] { let sectname = &self.s_name()[..]; match memchr::memchr(b'\0', sectname) { Some(end) => §name[..end], None => sectname, } } /// Return the offset and size of the section in the file. fn file_range(&self) -> Option<(u64, u64)> { Some((self.s_scnptr().into(), self.s_size().into())) } /// Return the section data. /// /// Returns `Ok(&[])` if the section has no data. /// Returns `Err` for invalid values. fn data<'data, R: ReadRef<'data>>(&self, data: R) -> result::Result<&'data [u8], ()> { if let Some((offset, size)) = self.file_range() { data.read_bytes_at(offset, size) } else { Ok(&[]) } } /// Read the relocations. fn relocations<'data, R: ReadRef<'data>>(&self, data: R) -> read::Result<&'data [Self::Rel]>; } impl SectionHeader for xcoff::SectionHeader32 { type Word = u32; type HalfWord = u16; type Xcoff = xcoff::FileHeader32; type Rel = xcoff::Rel32; fn s_name(&self) -> &[u8; 8] { &self.s_name } fn s_paddr(&self) -> Self::Word { self.s_paddr.get(BE) } fn s_vaddr(&self) -> Self::Word { self.s_vaddr.get(BE) } fn s_size(&self) -> Self::Word { self.s_size.get(BE) } fn s_scnptr(&self) -> Self::Word { self.s_scnptr.get(BE) } fn s_relptr(&self) -> Self::Word { self.s_relptr.get(BE) } fn s_lnnoptr(&self) -> Self::Word { self.s_lnnoptr.get(BE) } fn s_nreloc(&self) -> Self::HalfWord { self.s_nreloc.get(BE) } fn s_nlnno(&self) -> Self::HalfWord { self.s_nlnno.get(BE) } fn s_flags(&self) -> u32 { self.s_flags.get(BE) } /// Read the relocations in a XCOFF32 file. /// /// `data` must be the entire file data. fn relocations<'data, R: ReadRef<'data>>(&self, data: R) -> read::Result<&'data [Self::Rel]> { let reloc_num = self.s_nreloc() as usize; // TODO: If more than 65,534 relocation entries are required, the field value will be 65535, // and an STYP_OVRFLO section header will contain the actual count of relocation entries in // the s_paddr field. if reloc_num == 65535 { return Err(Error("Overflow section is not supported yet.")); } data.read_slice_at(self.s_relptr().into(), reloc_num) .read_error("Invalid XCOFF relocation offset or number") } } impl SectionHeader for xcoff::SectionHeader64 { type Word = u64; type HalfWord = u32; type Xcoff = xcoff::FileHeader64; type Rel = xcoff::Rel64; fn s_name(&self) -> &[u8; 8] { &self.s_name } fn s_paddr(&self) -> Self::Word { self.s_paddr.get(BE) } fn s_vaddr(&self) -> Self::Word { self.s_vaddr.get(BE) } fn s_size(&self) -> Self::Word { self.s_size.get(BE) } fn s_scnptr(&self) -> Self::Word { self.s_scnptr.get(BE) } fn s_relptr(&self) -> Self::Word { self.s_relptr.get(BE) } fn s_lnnoptr(&self) -> Self::Word { self.s_lnnoptr.get(BE) } fn s_nreloc(&self) -> Self::HalfWord { self.s_nreloc.get(BE) } fn s_nlnno(&self) -> Self::HalfWord { self.s_nlnno.get(BE) } fn s_flags(&self) -> u32 { self.s_flags.get(BE) } /// Read the relocations in a XCOFF64 file. /// /// `data` must be the entire file data. fn relocations<'data, R: ReadRef<'data>>(&self, data: R) -> read::Result<&'data [Self::Rel]> { data.read_slice_at(self.s_relptr(), self.s_nreloc() as usize) .read_error("Invalid XCOFF relocation offset or number") } } object-0.36.5/src/read/xcoff/segment.rs000064400000000000000000000057271046102023000160170ustar 00000000000000//! TODO: Support the segment for XCOFF when auxiliary file header and loader section is ready. use core::fmt::Debug; use core::str; use crate::read::{self, ObjectSegment, ReadRef, Result, SegmentFlags}; use crate::xcoff; use super::{FileHeader, XcoffFile}; /// An iterator for the segments in an [`XcoffFile32`](super::XcoffFile32). pub type XcoffSegmentIterator32<'data, 'file, R = &'data [u8]> = XcoffSegmentIterator<'data, 'file, xcoff::FileHeader32, R>; /// An iterator for the segments in an [`XcoffFile64`](super::XcoffFile64). pub type XcoffSegmentIterator64<'data, 'file, R = &'data [u8]> = XcoffSegmentIterator<'data, 'file, xcoff::FileHeader64, R>; /// An iterator for the segments in an [`XcoffFile`]. /// /// This is a stub that doesn't implement any functionality. #[derive(Debug)] pub struct XcoffSegmentIterator<'data, 'file, Xcoff, R = &'data [u8]> where Xcoff: FileHeader, R: ReadRef<'data>, { #[allow(unused)] pub(super) file: &'file XcoffFile<'data, Xcoff, R>, } impl<'data, 'file, Xcoff, R> Iterator for XcoffSegmentIterator<'data, 'file, Xcoff, R> where Xcoff: FileHeader, R: ReadRef<'data>, { type Item = XcoffSegment<'data, 'file, Xcoff, R>; fn next(&mut self) -> Option { None } } /// A segment in an [`XcoffFile32`](super::XcoffFile32). pub type XcoffSegment32<'data, 'file, R = &'data [u8]> = XcoffSegment<'data, 'file, xcoff::FileHeader32, R>; /// A segment in an [`XcoffFile64`](super::XcoffFile64). pub type XcoffSegment64<'data, 'file, R = &'data [u8]> = XcoffSegment<'data, 'file, xcoff::FileHeader64, R>; /// A loadable section in an [`XcoffFile`]. /// /// This is a stub that doesn't implement any functionality. #[derive(Debug)] pub struct XcoffSegment<'data, 'file, Xcoff, R = &'data [u8]> where Xcoff: FileHeader, R: ReadRef<'data>, { #[allow(unused)] pub(super) file: &'file XcoffFile<'data, Xcoff, R>, } impl<'data, 'file, Xcoff, R> XcoffSegment<'data, 'file, Xcoff, R> where Xcoff: FileHeader, R: ReadRef<'data>, { } impl<'data, 'file, Xcoff, R> read::private::Sealed for XcoffSegment<'data, 'file, Xcoff, R> where Xcoff: FileHeader, R: ReadRef<'data>, { } impl<'data, 'file, Xcoff, R> ObjectSegment<'data> for XcoffSegment<'data, 'file, Xcoff, R> where Xcoff: FileHeader, R: ReadRef<'data>, { fn address(&self) -> u64 { unreachable!(); } fn size(&self) -> u64 { unreachable!(); } fn align(&self) -> u64 { unreachable!(); } fn file_range(&self) -> (u64, u64) { unreachable!(); } fn data(&self) -> Result<&'data [u8]> { unreachable!(); } fn data_range(&self, _address: u64, _size: u64) -> Result> { unreachable!(); } fn name_bytes(&self) -> Result> { unreachable!(); } fn name(&self) -> Result> { unreachable!(); } fn flags(&self) -> SegmentFlags { unreachable!(); } } object-0.36.5/src/read/xcoff/symbol.rs000064400000000000000000000573461046102023000156660ustar 00000000000000use alloc::fmt; use core::convert::TryInto; use core::fmt::Debug; use core::marker::PhantomData; use core::str; use crate::endian::{BigEndian as BE, U32Bytes}; use crate::pod::{bytes_of, Pod}; use crate::read::{ self, Bytes, Error, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, Result, SectionIndex, StringTable, SymbolFlags, SymbolIndex, SymbolKind, SymbolScope, SymbolSection, }; use crate::xcoff; use super::{FileHeader, XcoffFile}; /// A table of symbol entries in an XCOFF file. /// /// Also includes the string table used for the symbol names. /// /// Returned by [`FileHeader::symbols`]. #[derive(Debug)] pub struct SymbolTable<'data, Xcoff, R = &'data [u8]> where Xcoff: FileHeader, R: ReadRef<'data>, { symbols: &'data [xcoff::SymbolBytes], strings: StringTable<'data, R>, header: PhantomData, } impl<'data, Xcoff, R> Default for SymbolTable<'data, Xcoff, R> where Xcoff: FileHeader, R: ReadRef<'data>, { fn default() -> Self { Self { symbols: &[], strings: StringTable::default(), header: PhantomData, } } } impl<'data, Xcoff, R> SymbolTable<'data, Xcoff, R> where Xcoff: FileHeader, R: ReadRef<'data>, { /// Parse the symbol table. pub fn parse(header: Xcoff, data: R) -> Result { let mut offset = header.f_symptr().into(); let (symbols, strings) = if offset != 0 { let symbols = data .read_slice(&mut offset, header.f_nsyms() as usize) .read_error("Invalid XCOFF symbol table offset or size")?; // Parse the string table. // Note: don't update data when reading length; the length includes itself. let length = data .read_at::>(offset) .read_error("Missing XCOFF string table")? .get(BE); let str_end = offset .checked_add(length as u64) .read_error("Invalid XCOFF string table length")?; let strings = StringTable::new(data, offset, str_end); (symbols, strings) } else { (&[][..], StringTable::default()) }; Ok(SymbolTable { symbols, strings, header: PhantomData, }) } /// Return the string table used for the symbol names. #[inline] pub fn strings(&self) -> StringTable<'data, R> { self.strings } /// Iterate over the symbols. /// /// This does not return null symbols. #[inline] pub fn iter<'table>(&'table self) -> SymbolIterator<'data, 'table, Xcoff, R> { SymbolIterator { symbols: self, index: 0, } } /// Empty symbol iterator. #[inline] pub(super) fn iter_none<'table>(&'table self) -> SymbolIterator<'data, 'table, Xcoff, R> { SymbolIterator { symbols: self, index: self.symbols.len(), } } /// Return the symbol entry at the given index and offset. pub fn get(&self, index: SymbolIndex, offset: usize) -> Result<&'data T> { let entry = index .0 .checked_add(offset) .and_then(|x| self.symbols.get(x)) .read_error("Invalid XCOFF symbol index")?; let bytes = bytes_of(entry); Bytes(bytes).read().read_error("Invalid XCOFF symbol data") } /// Get the symbol at the given index. /// /// This does not check if the symbol is null, but does check if the index is in bounds. fn symbol_unchecked(&self, index: SymbolIndex) -> Result<&'data Xcoff::Symbol> { self.get::(index, 0) } /// Get the symbol at the given index. /// /// Returns an error for null symbols and out of bounds indices. /// Note that this is unable to check whether the index is an auxiliary symbol. pub fn symbol(&self, index: SymbolIndex) -> Result<&'data Xcoff::Symbol> { let symbol = self.symbol_unchecked(index)?; if symbol.is_null() { return Err(Error("Invalid XCOFF symbol index")); } Ok(symbol) } /// Return a file auxiliary symbol. pub fn aux_file(&self, index: SymbolIndex, offset: usize) -> Result<&'data Xcoff::FileAux> { debug_assert!(self.symbol(index)?.has_aux_file()); let aux_file = self.get::(index, offset)?; if let Some(aux_type) = aux_file.x_auxtype() { if aux_type != xcoff::AUX_FILE { return Err(Error("Invalid index for file auxiliary symbol.")); } } Ok(aux_file) } /// Return the csect auxiliary symbol. pub fn aux_csect(&self, index: SymbolIndex, offset: usize) -> Result<&'data Xcoff::CsectAux> { debug_assert!(self.symbol(index)?.has_aux_csect()); let aux_csect = self.get::(index, offset)?; if let Some(aux_type) = aux_csect.x_auxtype() { if aux_type != xcoff::AUX_CSECT { return Err(Error("Invalid index/offset for csect auxiliary symbol.")); } } Ok(aux_csect) } /// Return true if the symbol table is empty. #[inline] pub fn is_empty(&self) -> bool { self.symbols.is_empty() } /// The number of symbol table entries. /// /// This includes auxiliary symbol table entries. #[inline] pub fn len(&self) -> usize { self.symbols.len() } } /// An iterator for symbol entries in an XCOFF file. /// /// Yields the index and symbol structure for each symbol. #[derive(Debug)] pub struct SymbolIterator<'data, 'table, Xcoff, R = &'data [u8]> where Xcoff: FileHeader, R: ReadRef<'data>, { symbols: &'table SymbolTable<'data, Xcoff, R>, index: usize, } impl<'data, 'table, Xcoff: FileHeader, R: ReadRef<'data>> Iterator for SymbolIterator<'data, 'table, Xcoff, R> { type Item = (SymbolIndex, &'data Xcoff::Symbol); fn next(&mut self) -> Option { loop { let index = SymbolIndex(self.index); let symbol = self.symbols.symbol_unchecked(index).ok()?; self.index += 1 + symbol.n_numaux() as usize; if !symbol.is_null() { return Some((index, symbol)); } } } } /// A symbol table in an [`XcoffFile32`](super::XcoffFile32). pub type XcoffSymbolTable32<'data, 'file, R = &'data [u8]> = XcoffSymbolTable<'data, 'file, xcoff::FileHeader32, R>; /// A symbol table in an [`XcoffFile64`](super::XcoffFile64). pub type XcoffSymbolTable64<'data, 'file, R = &'data [u8]> = XcoffSymbolTable<'data, 'file, xcoff::FileHeader64, R>; /// A symbol table in an [`XcoffFile`]. #[derive(Debug, Clone, Copy)] pub struct XcoffSymbolTable<'data, 'file, Xcoff, R = &'data [u8]> where Xcoff: FileHeader, R: ReadRef<'data>, { pub(super) file: &'file XcoffFile<'data, Xcoff, R>, pub(super) symbols: &'file SymbolTable<'data, Xcoff, R>, } impl<'data, 'file, Xcoff: FileHeader, R: ReadRef<'data>> read::private::Sealed for XcoffSymbolTable<'data, 'file, Xcoff, R> { } impl<'data, 'file, Xcoff: FileHeader, R: ReadRef<'data>> ObjectSymbolTable<'data> for XcoffSymbolTable<'data, 'file, Xcoff, R> { type Symbol = XcoffSymbol<'data, 'file, Xcoff, R>; type SymbolIterator = XcoffSymbolIterator<'data, 'file, Xcoff, R>; fn symbols(&self) -> Self::SymbolIterator { XcoffSymbolIterator { file: self.file, symbols: self.symbols.iter(), } } fn symbol_by_index(&self, index: SymbolIndex) -> read::Result { let symbol = self.symbols.symbol(index)?; Ok(XcoffSymbol { file: self.file, symbols: self.symbols, index, symbol, }) } } /// An iterator for the symbols in an [`XcoffFile32`](super::XcoffFile32). pub type XcoffSymbolIterator32<'data, 'file, R = &'data [u8]> = XcoffSymbolIterator<'data, 'file, xcoff::FileHeader32, R>; /// An iterator for the symbols in an [`XcoffFile64`](super::XcoffFile64). pub type XcoffSymbolIterator64<'data, 'file, R = &'data [u8]> = XcoffSymbolIterator<'data, 'file, xcoff::FileHeader64, R>; /// An iterator for the symbols in an [`XcoffFile`]. pub struct XcoffSymbolIterator<'data, 'file, Xcoff, R = &'data [u8]> where Xcoff: FileHeader, R: ReadRef<'data>, { pub(super) file: &'file XcoffFile<'data, Xcoff, R>, pub(super) symbols: SymbolIterator<'data, 'file, Xcoff, R>, } impl<'data, 'file, Xcoff: FileHeader, R: ReadRef<'data>> fmt::Debug for XcoffSymbolIterator<'data, 'file, Xcoff, R> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("XcoffSymbolIterator").finish() } } impl<'data, 'file, Xcoff: FileHeader, R: ReadRef<'data>> Iterator for XcoffSymbolIterator<'data, 'file, Xcoff, R> { type Item = XcoffSymbol<'data, 'file, Xcoff, R>; fn next(&mut self) -> Option { let (index, symbol) = self.symbols.next()?; Some(XcoffSymbol { file: self.file, symbols: self.symbols.symbols, index, symbol, }) } } /// A symbol in an [`XcoffFile32`](super::XcoffFile32). pub type XcoffSymbol32<'data, 'file, R = &'data [u8]> = XcoffSymbol<'data, 'file, xcoff::FileHeader32, R>; /// A symbol in an [`XcoffFile64`](super::XcoffFile64). pub type XcoffSymbol64<'data, 'file, R = &'data [u8]> = XcoffSymbol<'data, 'file, xcoff::FileHeader64, R>; /// A symbol in an [`XcoffFile`]. /// /// Most functionality is provided by the [`ObjectSymbol`] trait implementation. #[derive(Debug, Clone, Copy)] pub struct XcoffSymbol<'data, 'file, Xcoff, R = &'data [u8]> where Xcoff: FileHeader, R: ReadRef<'data>, { pub(super) file: &'file XcoffFile<'data, Xcoff, R>, pub(super) symbols: &'file SymbolTable<'data, Xcoff, R>, pub(super) index: SymbolIndex, pub(super) symbol: &'data Xcoff::Symbol, } impl<'data, 'file, Xcoff, R> XcoffSymbol<'data, 'file, Xcoff, R> where Xcoff: FileHeader, R: ReadRef<'data>, { /// Get the XCOFF file containing this symbol. pub fn xcoff_file(&self) -> &'file XcoffFile<'data, Xcoff, R> { self.file } /// Get the raw XCOFF symbol structure. pub fn xcoff_symbol(&self) -> &'data Xcoff::Symbol { self.symbol } } impl<'data, 'file, Xcoff: FileHeader, R: ReadRef<'data>> read::private::Sealed for XcoffSymbol<'data, 'file, Xcoff, R> { } impl<'data, 'file, Xcoff: FileHeader, R: ReadRef<'data>> ObjectSymbol<'data> for XcoffSymbol<'data, 'file, Xcoff, R> { #[inline] fn index(&self) -> SymbolIndex { self.index } fn name_bytes(&self) -> Result<&'data [u8]> { if self.symbol.has_aux_file() { // By convention the file name is in the first auxiliary entry. self.symbols .aux_file(self.index, 1)? .fname(self.symbols.strings) } else { self.symbol.name(self.symbols.strings) } } fn name(&self) -> Result<&'data str> { let name = self.name_bytes()?; str::from_utf8(name) .ok() .read_error("Non UTF-8 XCOFF symbol name") } #[inline] fn address(&self) -> u64 { match self.symbol.n_sclass() { // Relocatable address. xcoff::C_EXT | xcoff::C_WEAKEXT | xcoff::C_HIDEXT | xcoff::C_FCN | xcoff::C_BLOCK | xcoff::C_STAT | xcoff::C_INFO => self.symbol.n_value().into(), _ => 0, } } #[inline] fn size(&self) -> u64 { if self.symbol.has_aux_csect() { // XCOFF32 must have the csect auxiliary entry as the last auxiliary entry. // XCOFF64 doesn't require this, but conventionally does. if let Ok(aux_csect) = self .file .symbols .aux_csect(self.index, self.symbol.n_numaux() as usize) { let sym_type = aux_csect.sym_type(); if sym_type == xcoff::XTY_SD || sym_type == xcoff::XTY_CM { return aux_csect.x_scnlen(); } } } 0 } fn kind(&self) -> SymbolKind { if self.symbol.has_aux_csect() { if let Ok(aux_csect) = self .file .symbols .aux_csect(self.index, self.symbol.n_numaux() as usize) { let sym_type = aux_csect.sym_type(); if sym_type == xcoff::XTY_SD || sym_type == xcoff::XTY_CM { return match aux_csect.x_smclas() { xcoff::XMC_PR | xcoff::XMC_GL => SymbolKind::Text, xcoff::XMC_RO | xcoff::XMC_RW | xcoff::XMC_TD | xcoff::XMC_BS => { SymbolKind::Data } xcoff::XMC_TL | xcoff::XMC_UL => SymbolKind::Tls, xcoff::XMC_DS | xcoff::XMC_TC0 | xcoff::XMC_TC => { // `Metadata` might be a better kind for these if we had it. SymbolKind::Data } _ => SymbolKind::Unknown, }; } else if sym_type == xcoff::XTY_LD { // A function entry point. Neither `Text` nor `Label` are a good fit for this. return SymbolKind::Text; } else if sym_type == xcoff::XTY_ER { return SymbolKind::Unknown; } } } match self.symbol.n_sclass() { xcoff::C_FILE => SymbolKind::File, _ => SymbolKind::Unknown, } } fn section(&self) -> SymbolSection { match self.symbol.n_scnum() { xcoff::N_ABS => SymbolSection::Absolute, xcoff::N_UNDEF => SymbolSection::Undefined, xcoff::N_DEBUG => SymbolSection::None, index if index > 0 => SymbolSection::Section(SectionIndex(index as usize)), _ => SymbolSection::Unknown, } } #[inline] fn is_undefined(&self) -> bool { self.symbol.is_undefined() } /// Return true if the symbol is a definition of a function or data object. #[inline] fn is_definition(&self) -> bool { if self.symbol.n_scnum() <= 0 { return false; } if self.symbol.has_aux_csect() { if let Ok(aux_csect) = self .symbols .aux_csect(self.index, self.symbol.n_numaux() as usize) { let sym_type = aux_csect.sym_type(); sym_type == xcoff::XTY_SD || sym_type == xcoff::XTY_LD || sym_type == xcoff::XTY_CM } else { false } } else { false } } #[inline] fn is_common(&self) -> bool { self.symbol.n_sclass() == xcoff::C_EXT && self.symbol.n_scnum() == xcoff::N_UNDEF } #[inline] fn is_weak(&self) -> bool { self.symbol.n_sclass() == xcoff::C_WEAKEXT } fn scope(&self) -> SymbolScope { if self.symbol.n_scnum() == xcoff::N_UNDEF { SymbolScope::Unknown } else { match self.symbol.n_sclass() { xcoff::C_EXT | xcoff::C_WEAKEXT => { let visibility = self.symbol.n_type() & xcoff::SYM_V_MASK; if visibility == xcoff::SYM_V_HIDDEN { SymbolScope::Linkage } else { SymbolScope::Dynamic } } _ => SymbolScope::Compilation, } } } #[inline] fn is_global(&self) -> bool { match self.symbol.n_sclass() { xcoff::C_EXT | xcoff::C_WEAKEXT => true, _ => false, } } #[inline] fn is_local(&self) -> bool { !self.is_global() } #[inline] fn flags(&self) -> SymbolFlags { let mut x_smtyp = 0; let mut x_smclas = 0; let mut containing_csect = None; if self.symbol.has_aux_csect() { if let Ok(aux_csect) = self .file .symbols .aux_csect(self.index, self.symbol.n_numaux() as usize) { x_smtyp = aux_csect.x_smtyp(); x_smclas = aux_csect.x_smclas(); if aux_csect.sym_type() == xcoff::XTY_LD { containing_csect = Some(SymbolIndex(aux_csect.x_scnlen() as usize)) } } } SymbolFlags::Xcoff { n_sclass: self.symbol.n_sclass(), x_smtyp, x_smclas, containing_csect, } } } /// A trait for generic access to [`xcoff::Symbol32`] and [`xcoff::Symbol64`]. #[allow(missing_docs)] pub trait Symbol: Debug + Pod { type Word: Into; fn n_value(&self) -> Self::Word; fn n_scnum(&self) -> i16; fn n_type(&self) -> u16; fn n_sclass(&self) -> u8; fn n_numaux(&self) -> u8; fn name_offset(&self) -> Option; fn name<'data, R: ReadRef<'data>>( &'data self, strings: StringTable<'data, R>, ) -> Result<&'data [u8]>; /// Return the section index for the symbol. fn section(&self) -> Option { let index = self.n_scnum(); if index > 0 { Some(SectionIndex(index as usize)) } else { None } } /// Return true if the symbol is a null placeholder. #[inline] fn is_null(&self) -> bool { self.n_sclass() == xcoff::C_NULL } /// Return true if the symbol is undefined. #[inline] fn is_undefined(&self) -> bool { let n_sclass = self.n_sclass(); (n_sclass == xcoff::C_EXT || n_sclass == xcoff::C_WEAKEXT) && self.n_scnum() == xcoff::N_UNDEF } /// Return true if the symbol has file auxiliary entry. fn has_aux_file(&self) -> bool { self.n_numaux() > 0 && self.n_sclass() == xcoff::C_FILE } /// Return true if the symbol has csect auxiliary entry. /// /// A csect auxiliary entry is required for each symbol table entry that has /// a storage class value of C_EXT, C_WEAKEXT, or C_HIDEXT. fn has_aux_csect(&self) -> bool { let sclass = self.n_sclass(); self.n_numaux() > 0 && (sclass == xcoff::C_EXT || sclass == xcoff::C_WEAKEXT || sclass == xcoff::C_HIDEXT) } } impl Symbol for xcoff::Symbol64 { type Word = u64; fn n_value(&self) -> Self::Word { self.n_value.get(BE) } fn n_scnum(&self) -> i16 { self.n_scnum.get(BE) } fn n_type(&self) -> u16 { self.n_type.get(BE) } fn n_sclass(&self) -> u8 { self.n_sclass } fn n_numaux(&self) -> u8 { self.n_numaux } fn name_offset(&self) -> Option { Some(self.n_offset.get(BE)) } /// Parse the symbol name for XCOFF64. fn name<'data, R: ReadRef<'data>>( &'data self, strings: StringTable<'data, R>, ) -> Result<&'data [u8]> { strings .get(self.n_offset.get(BE)) .read_error("Invalid XCOFF symbol name offset") } } impl Symbol for xcoff::Symbol32 { type Word = u32; fn n_value(&self) -> Self::Word { self.n_value.get(BE) } fn n_scnum(&self) -> i16 { self.n_scnum.get(BE) } fn n_type(&self) -> u16 { self.n_type.get(BE) } fn n_sclass(&self) -> u8 { self.n_sclass } fn n_numaux(&self) -> u8 { self.n_numaux } fn name_offset(&self) -> Option { if self.n_name[0] == 0 { let offset = u32::from_be_bytes(self.n_name[4..8].try_into().unwrap()); Some(offset) } else { None } } /// Parse the symbol name for XCOFF32. fn name<'data, R: ReadRef<'data>>( &'data self, strings: StringTable<'data, R>, ) -> Result<&'data [u8]> { if let Some(offset) = self.name_offset() { // If the name starts with 0 then the last 4 bytes are a string table offset. strings .get(offset) .read_error("Invalid XCOFF symbol name offset") } else { // The name is inline and padded with nulls. Ok(match memchr::memchr(b'\0', &self.n_name) { Some(end) => &self.n_name[..end], None => &self.n_name, }) } } } /// A trait for generic access to [`xcoff::FileAux32`] and [`xcoff::FileAux64`]. #[allow(missing_docs)] pub trait FileAux: Debug + Pod { fn x_fname(&self) -> &[u8; 8]; fn x_ftype(&self) -> u8; fn x_auxtype(&self) -> Option; fn name_offset(&self) -> Option { let x_fname = self.x_fname(); if x_fname[0] == 0 { Some(u32::from_be_bytes(x_fname[4..8].try_into().unwrap())) } else { None } } /// Parse the x_fname field, which may be an inline string or a string table offset. fn fname<'data, R: ReadRef<'data>>( &'data self, strings: StringTable<'data, R>, ) -> Result<&'data [u8]> { if let Some(offset) = self.name_offset() { // If the name starts with 0 then the last 4 bytes are a string table offset. strings .get(offset) .read_error("Invalid XCOFF symbol name offset") } else { // The name is inline and padded with nulls. let x_fname = self.x_fname(); Ok(match memchr::memchr(b'\0', x_fname) { Some(end) => &x_fname[..end], None => x_fname, }) } } } impl FileAux for xcoff::FileAux64 { fn x_fname(&self) -> &[u8; 8] { &self.x_fname } fn x_ftype(&self) -> u8 { self.x_ftype } fn x_auxtype(&self) -> Option { Some(self.x_auxtype) } } impl FileAux for xcoff::FileAux32 { fn x_fname(&self) -> &[u8; 8] { &self.x_fname } fn x_ftype(&self) -> u8 { self.x_ftype } fn x_auxtype(&self) -> Option { None } } /// A trait for generic access to [`xcoff::CsectAux32`] and [`xcoff::CsectAux64`]. #[allow(missing_docs)] pub trait CsectAux: Debug + Pod { fn x_scnlen(&self) -> u64; fn x_parmhash(&self) -> u32; fn x_snhash(&self) -> u16; fn x_smtyp(&self) -> u8; fn x_smclas(&self) -> u8; fn x_stab(&self) -> Option; fn x_snstab(&self) -> Option; fn x_auxtype(&self) -> Option; fn alignment(&self) -> u8 { self.x_smtyp() >> 3 } fn sym_type(&self) -> u8 { self.x_smtyp() & 0x07 } } impl CsectAux for xcoff::CsectAux64 { fn x_scnlen(&self) -> u64 { self.x_scnlen_lo.get(BE) as u64 | ((self.x_scnlen_hi.get(BE) as u64) << 32) } fn x_parmhash(&self) -> u32 { self.x_parmhash.get(BE) } fn x_snhash(&self) -> u16 { self.x_snhash.get(BE) } fn x_smtyp(&self) -> u8 { self.x_smtyp } fn x_smclas(&self) -> u8 { self.x_smclas } fn x_stab(&self) -> Option { None } fn x_snstab(&self) -> Option { None } fn x_auxtype(&self) -> Option { Some(self.x_auxtype) } } impl CsectAux for xcoff::CsectAux32 { fn x_scnlen(&self) -> u64 { self.x_scnlen.get(BE) as u64 } fn x_parmhash(&self) -> u32 { self.x_parmhash.get(BE) } fn x_snhash(&self) -> u16 { self.x_snhash.get(BE) } fn x_smtyp(&self) -> u8 { self.x_smtyp } fn x_smclas(&self) -> u8 { self.x_smclas } fn x_stab(&self) -> Option { Some(self.x_stab.get(BE)) } fn x_snstab(&self) -> Option { Some(self.x_snstab.get(BE)) } fn x_auxtype(&self) -> Option { None } } object-0.36.5/src/write/coff/mod.rs000064400000000000000000000004061046102023000151500ustar 00000000000000//! Support for writing COFF files. //! //! Provides [`Writer`] for low level writing of COFF files. //! This is also used to provide COFF support for [`write::Object`](crate::write::Object). mod object; pub use self::object::*; mod writer; pub use writer::*; object-0.36.5/src/write/coff/object.rs000064400000000000000000000704221046102023000156440ustar 00000000000000use alloc::vec::Vec; use crate::pe as coff; use crate::write::coff::writer; use crate::write::util::*; use crate::write::*; #[derive(Default, Clone, Copy)] struct SectionOffsets { name: writer::Name, offset: u32, reloc_offset: u32, selection: u8, associative_section: u32, } #[derive(Default, Clone, Copy)] struct SymbolOffsets { name: writer::Name, index: u32, aux_count: u8, } /// Internal format to use for the `.drectve` section containing linker /// directives for symbol exports. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum CoffExportStyle { /// MSVC format supported by link.exe and LLD. Msvc, /// Gnu format supported by GNU LD and LLD. Gnu, } impl<'a> Object<'a> { pub(crate) fn coff_section_info( &self, section: StandardSection, ) -> (&'static [u8], &'static [u8], SectionKind, SectionFlags) { match section { StandardSection::Text => (&[], &b".text"[..], SectionKind::Text, SectionFlags::None), StandardSection::Data => (&[], &b".data"[..], SectionKind::Data, SectionFlags::None), StandardSection::ReadOnlyData | StandardSection::ReadOnlyDataWithRel | StandardSection::ReadOnlyString => ( &[], &b".rdata"[..], SectionKind::ReadOnlyData, SectionFlags::None, ), StandardSection::UninitializedData => ( &[], &b".bss"[..], SectionKind::UninitializedData, SectionFlags::None, ), // TLS sections are data sections with a special name. StandardSection::Tls => (&[], &b".tls$"[..], SectionKind::Data, SectionFlags::None), StandardSection::UninitializedTls => { // Unsupported section. (&[], &[], SectionKind::UninitializedTls, SectionFlags::None) } StandardSection::TlsVariables => { // Unsupported section. (&[], &[], SectionKind::TlsVariables, SectionFlags::None) } StandardSection::Common => { // Unsupported section. (&[], &[], SectionKind::Common, SectionFlags::None) } StandardSection::GnuProperty => { // Unsupported section. (&[], &[], SectionKind::Note, SectionFlags::None) } } } pub(crate) fn coff_subsection_name(&self, section: &[u8], value: &[u8]) -> Vec { let mut name = section.to_vec(); name.push(b'$'); name.extend_from_slice(value); name } pub(crate) fn coff_translate_relocation(&mut self, reloc: &mut Relocation) -> Result<()> { use RelocationEncoding as E; use RelocationKind as K; let (mut kind, encoding, size) = if let RelocationFlags::Generic { kind, encoding, size, } = reloc.flags { (kind, encoding, size) } else { return Ok(()); }; if kind == K::GotRelative { // Use a stub symbol for the relocation instead. // This isn't really a GOT, but it's a similar purpose. // TODO: need to handle DLL imports differently? kind = K::Relative; reloc.symbol = self.coff_add_stub_symbol(reloc.symbol)?; } else if kind == K::PltRelative { // Windows doesn't need a separate relocation type for // references to functions in import libraries. // For convenience, treat this the same as Relative. kind = K::Relative; } let unsupported_reloc = || Err(Error(format!("unimplemented relocation {:?}", reloc))); let typ = match self.architecture { Architecture::I386 => match (kind, size) { (K::Absolute, 16) => coff::IMAGE_REL_I386_DIR16, (K::Relative, 16) => coff::IMAGE_REL_I386_REL16, (K::Absolute, 32) => coff::IMAGE_REL_I386_DIR32, (K::ImageOffset, 32) => coff::IMAGE_REL_I386_DIR32NB, (K::SectionIndex, 16) => coff::IMAGE_REL_I386_SECTION, (K::SectionOffset, 32) => coff::IMAGE_REL_I386_SECREL, (K::SectionOffset, 7) => coff::IMAGE_REL_I386_SECREL7, (K::Relative, 32) => coff::IMAGE_REL_I386_REL32, _ => return unsupported_reloc(), }, Architecture::X86_64 => match (kind, size) { (K::Absolute, 64) => coff::IMAGE_REL_AMD64_ADDR64, (K::Absolute, 32) => coff::IMAGE_REL_AMD64_ADDR32, (K::ImageOffset, 32) => coff::IMAGE_REL_AMD64_ADDR32NB, (K::Relative, 32) => match reloc.addend { -5 => coff::IMAGE_REL_AMD64_REL32_1, -6 => coff::IMAGE_REL_AMD64_REL32_2, -7 => coff::IMAGE_REL_AMD64_REL32_3, -8 => coff::IMAGE_REL_AMD64_REL32_4, -9 => coff::IMAGE_REL_AMD64_REL32_5, _ => coff::IMAGE_REL_AMD64_REL32, }, (K::SectionIndex, 16) => coff::IMAGE_REL_AMD64_SECTION, (K::SectionOffset, 32) => coff::IMAGE_REL_AMD64_SECREL, (K::SectionOffset, 7) => coff::IMAGE_REL_AMD64_SECREL7, _ => return unsupported_reloc(), }, Architecture::Arm => match (kind, size) { (K::Absolute, 32) => coff::IMAGE_REL_ARM_ADDR32, (K::ImageOffset, 32) => coff::IMAGE_REL_ARM_ADDR32NB, (K::Relative, 32) => coff::IMAGE_REL_ARM_REL32, (K::SectionIndex, 16) => coff::IMAGE_REL_ARM_SECTION, (K::SectionOffset, 32) => coff::IMAGE_REL_ARM_SECREL, _ => return unsupported_reloc(), }, Architecture::Aarch64 => match (kind, encoding, size) { (K::Absolute, _, 32) => coff::IMAGE_REL_ARM64_ADDR32, (K::ImageOffset, _, 32) => coff::IMAGE_REL_ARM64_ADDR32NB, (K::SectionIndex, _, 16) => coff::IMAGE_REL_ARM64_SECTION, (K::SectionOffset, _, 32) => coff::IMAGE_REL_ARM64_SECREL, (K::Absolute, _, 64) => coff::IMAGE_REL_ARM64_ADDR64, (K::Relative, _, 32) => coff::IMAGE_REL_ARM64_REL32, (K::Relative, E::AArch64Call, 26) => coff::IMAGE_REL_ARM64_BRANCH26, _ => return unsupported_reloc(), }, _ => { return Err(Error(format!( "unimplemented architecture {:?}", self.architecture ))); } }; reloc.flags = RelocationFlags::Coff { typ }; Ok(()) } pub(crate) fn coff_adjust_addend(&self, relocation: &mut Relocation) -> Result { let typ = if let RelocationFlags::Coff { typ } = relocation.flags { typ } else { return Err(Error(format!("invalid relocation flags {:?}", relocation))); }; let offset = match self.architecture { Architecture::Arm => { if typ == coff::IMAGE_REL_ARM_REL32 { 4 } else { 0 } } Architecture::Aarch64 => { if typ == coff::IMAGE_REL_ARM64_REL32 { 4 } else { 0 } } Architecture::I386 => { if typ == coff::IMAGE_REL_I386_REL32 { 4 } else { 0 } } Architecture::X86_64 => match typ { coff::IMAGE_REL_AMD64_REL32 => 4, coff::IMAGE_REL_AMD64_REL32_1 => 5, coff::IMAGE_REL_AMD64_REL32_2 => 6, coff::IMAGE_REL_AMD64_REL32_3 => 7, coff::IMAGE_REL_AMD64_REL32_4 => 8, coff::IMAGE_REL_AMD64_REL32_5 => 9, _ => 0, }, _ => return Err(Error(format!("unimplemented relocation {:?}", relocation))), }; relocation.addend += offset; Ok(true) } pub(crate) fn coff_relocation_size(&self, reloc: &Relocation) -> Result { let typ = if let RelocationFlags::Coff { typ } = reloc.flags { typ } else { return Err(Error(format!("unexpected relocation for size {:?}", reloc))); }; let size = match self.architecture { Architecture::I386 => match typ { coff::IMAGE_REL_I386_DIR16 | coff::IMAGE_REL_I386_REL16 | coff::IMAGE_REL_I386_SECTION => Some(16), coff::IMAGE_REL_I386_DIR32 | coff::IMAGE_REL_I386_DIR32NB | coff::IMAGE_REL_I386_SECREL | coff::IMAGE_REL_I386_TOKEN | coff::IMAGE_REL_I386_REL32 => Some(32), _ => None, }, Architecture::X86_64 => match typ { coff::IMAGE_REL_AMD64_SECTION => Some(16), coff::IMAGE_REL_AMD64_ADDR32 | coff::IMAGE_REL_AMD64_ADDR32NB | coff::IMAGE_REL_AMD64_REL32 | coff::IMAGE_REL_AMD64_REL32_1 | coff::IMAGE_REL_AMD64_REL32_2 | coff::IMAGE_REL_AMD64_REL32_3 | coff::IMAGE_REL_AMD64_REL32_4 | coff::IMAGE_REL_AMD64_REL32_5 | coff::IMAGE_REL_AMD64_SECREL | coff::IMAGE_REL_AMD64_TOKEN => Some(32), coff::IMAGE_REL_AMD64_ADDR64 => Some(64), _ => None, }, Architecture::Arm => match typ { coff::IMAGE_REL_ARM_SECTION => Some(16), coff::IMAGE_REL_ARM_ADDR32 | coff::IMAGE_REL_ARM_ADDR32NB | coff::IMAGE_REL_ARM_TOKEN | coff::IMAGE_REL_ARM_REL32 | coff::IMAGE_REL_ARM_SECREL => Some(32), _ => None, }, Architecture::Aarch64 => match typ { coff::IMAGE_REL_ARM64_SECTION => Some(16), coff::IMAGE_REL_ARM64_ADDR32 | coff::IMAGE_REL_ARM64_ADDR32NB | coff::IMAGE_REL_ARM64_SECREL | coff::IMAGE_REL_ARM64_TOKEN | coff::IMAGE_REL_ARM64_REL32 => Some(32), coff::IMAGE_REL_ARM64_ADDR64 => Some(64), _ => None, }, _ => None, }; size.ok_or_else(|| Error(format!("unsupported relocation for size {:?}", reloc))) } fn coff_add_stub_symbol(&mut self, symbol_id: SymbolId) -> Result { if let Some(stub_id) = self.stub_symbols.get(&symbol_id) { return Ok(*stub_id); } let stub_size = self.architecture.address_size().unwrap().bytes(); let name = b".rdata$.refptr".to_vec(); let section_id = self.add_section(Vec::new(), name, SectionKind::ReadOnlyData); let section = self.section_mut(section_id); section.set_data(vec![0; stub_size as usize], u64::from(stub_size)); self.add_relocation( section_id, Relocation { offset: 0, symbol: symbol_id, addend: 0, flags: RelocationFlags::Generic { kind: RelocationKind::Absolute, encoding: RelocationEncoding::Generic, size: stub_size * 8, }, }, )?; let mut name = b".refptr.".to_vec(); name.extend_from_slice(&self.symbol(symbol_id).name); let stub_id = self.add_raw_symbol(Symbol { name, value: 0, size: u64::from(stub_size), kind: SymbolKind::Data, scope: SymbolScope::Compilation, weak: false, section: SymbolSection::Section(section_id), flags: SymbolFlags::None, }); self.stub_symbols.insert(symbol_id, stub_id); Ok(stub_id) } /// Appends linker directives to the `.drectve` section to tell the linker /// to export all symbols with `SymbolScope::Dynamic`. /// /// This must be called after all symbols have been defined. pub fn add_coff_exports(&mut self, style: CoffExportStyle) { assert_eq!(self.format, BinaryFormat::Coff); let mut directives = vec![]; for symbol in &self.symbols { if symbol.scope == SymbolScope::Dynamic { match style { CoffExportStyle::Msvc => directives.extend(b" /EXPORT:\""), CoffExportStyle::Gnu => directives.extend(b" -export:\""), } directives.extend(&symbol.name); directives.extend(b"\""); if symbol.kind != SymbolKind::Text { match style { CoffExportStyle::Msvc => directives.extend(b",DATA"), CoffExportStyle::Gnu => directives.extend(b",data"), } } } } let drectve = self.add_section(vec![], b".drectve".to_vec(), SectionKind::Linker); self.append_section_data(drectve, &directives, 1); } pub(crate) fn coff_write(&self, buffer: &mut dyn WritableBuffer) -> Result<()> { let mut writer = writer::Writer::new(buffer); // Add section strings to strtab. let mut section_offsets = vec![SectionOffsets::default(); self.sections.len()]; for (index, section) in self.sections.iter().enumerate() { section_offsets[index].name = writer.add_name(§ion.name); } // Set COMDAT flags. for comdat in &self.comdats { let symbol = &self.symbols[comdat.symbol.0]; let comdat_section = match symbol.section { SymbolSection::Section(id) => id.0, _ => { return Err(Error(format!( "unsupported COMDAT symbol `{}` section {:?}", symbol.name().unwrap_or(""), symbol.section ))); } }; section_offsets[comdat_section].selection = match comdat.kind { ComdatKind::NoDuplicates => coff::IMAGE_COMDAT_SELECT_NODUPLICATES, ComdatKind::Any => coff::IMAGE_COMDAT_SELECT_ANY, ComdatKind::SameSize => coff::IMAGE_COMDAT_SELECT_SAME_SIZE, ComdatKind::ExactMatch => coff::IMAGE_COMDAT_SELECT_EXACT_MATCH, ComdatKind::Largest => coff::IMAGE_COMDAT_SELECT_LARGEST, ComdatKind::Newest => coff::IMAGE_COMDAT_SELECT_NEWEST, ComdatKind::Unknown => { return Err(Error(format!( "unsupported COMDAT symbol `{}` kind {:?}", symbol.name().unwrap_or(""), comdat.kind ))); } }; for id in &comdat.sections { let section = &self.sections[id.0]; if section.symbol.is_none() { return Err(Error(format!( "missing symbol for COMDAT section `{}`", section.name().unwrap_or(""), ))); } if id.0 != comdat_section { section_offsets[id.0].selection = coff::IMAGE_COMDAT_SELECT_ASSOCIATIVE; section_offsets[id.0].associative_section = comdat_section as u32 + 1; } } } // Reserve symbol indices and add symbol strings to strtab. let mut symbol_offsets = vec![SymbolOffsets::default(); self.symbols.len()]; for (index, symbol) in self.symbols.iter().enumerate() { symbol_offsets[index].index = writer.reserve_symbol_index(); let mut name = &*symbol.name; match symbol.kind { SymbolKind::File => { // Name goes in auxiliary symbol records. symbol_offsets[index].aux_count = writer.reserve_aux_file_name(&symbol.name); name = b".file"; } SymbolKind::Section if symbol.section.id().is_some() => { symbol_offsets[index].aux_count = writer.reserve_aux_section(); } _ => {} }; symbol_offsets[index].name = writer.add_name(name); } // Reserve file ranges. writer.reserve_file_header(); writer.reserve_section_headers(self.sections.len() as u16); for (index, section) in self.sections.iter().enumerate() { section_offsets[index].offset = writer.reserve_section(section.data.len()); section_offsets[index].reloc_offset = writer.reserve_relocations(section.relocations.len()); } writer.reserve_symtab_strtab(); // Start writing. writer.write_file_header(writer::FileHeader { machine: match (self.architecture, self.sub_architecture) { (Architecture::Arm, None) => coff::IMAGE_FILE_MACHINE_ARMNT, (Architecture::Aarch64, None) => coff::IMAGE_FILE_MACHINE_ARM64, (Architecture::Aarch64, Some(SubArchitecture::Arm64EC)) => { coff::IMAGE_FILE_MACHINE_ARM64EC } (Architecture::I386, None) => coff::IMAGE_FILE_MACHINE_I386, (Architecture::X86_64, None) => coff::IMAGE_FILE_MACHINE_AMD64, _ => { return Err(Error(format!( "unimplemented architecture {:?} with sub-architecture {:?}", self.architecture, self.sub_architecture ))); } }, time_date_stamp: 0, characteristics: match self.flags { FileFlags::Coff { characteristics } => characteristics, _ => 0, }, })?; // Write section headers. for (index, section) in self.sections.iter().enumerate() { let mut characteristics = if let SectionFlags::Coff { characteristics, .. } = section.flags { characteristics } else { match section.kind { SectionKind::Text => { coff::IMAGE_SCN_CNT_CODE | coff::IMAGE_SCN_MEM_EXECUTE | coff::IMAGE_SCN_MEM_READ } SectionKind::Data => { coff::IMAGE_SCN_CNT_INITIALIZED_DATA | coff::IMAGE_SCN_MEM_READ | coff::IMAGE_SCN_MEM_WRITE } SectionKind::UninitializedData => { coff::IMAGE_SCN_CNT_UNINITIALIZED_DATA | coff::IMAGE_SCN_MEM_READ | coff::IMAGE_SCN_MEM_WRITE } SectionKind::ReadOnlyData | SectionKind::ReadOnlyDataWithRel | SectionKind::ReadOnlyString => { coff::IMAGE_SCN_CNT_INITIALIZED_DATA | coff::IMAGE_SCN_MEM_READ } SectionKind::Debug | SectionKind::DebugString | SectionKind::Other | SectionKind::OtherString => { coff::IMAGE_SCN_CNT_INITIALIZED_DATA | coff::IMAGE_SCN_MEM_READ | coff::IMAGE_SCN_MEM_DISCARDABLE } SectionKind::Linker => coff::IMAGE_SCN_LNK_INFO | coff::IMAGE_SCN_LNK_REMOVE, SectionKind::Common | SectionKind::Tls | SectionKind::UninitializedTls | SectionKind::TlsVariables | SectionKind::Note | SectionKind::Unknown | SectionKind::Metadata | SectionKind::Elf(_) => { return Err(Error(format!( "unimplemented section `{}` kind {:?}", section.name().unwrap_or(""), section.kind ))); } } }; if section_offsets[index].selection != 0 { characteristics |= coff::IMAGE_SCN_LNK_COMDAT; }; if section.relocations.len() > 0xffff { characteristics |= coff::IMAGE_SCN_LNK_NRELOC_OVFL; } characteristics |= match section.align { 1 => coff::IMAGE_SCN_ALIGN_1BYTES, 2 => coff::IMAGE_SCN_ALIGN_2BYTES, 4 => coff::IMAGE_SCN_ALIGN_4BYTES, 8 => coff::IMAGE_SCN_ALIGN_8BYTES, 16 => coff::IMAGE_SCN_ALIGN_16BYTES, 32 => coff::IMAGE_SCN_ALIGN_32BYTES, 64 => coff::IMAGE_SCN_ALIGN_64BYTES, 128 => coff::IMAGE_SCN_ALIGN_128BYTES, 256 => coff::IMAGE_SCN_ALIGN_256BYTES, 512 => coff::IMAGE_SCN_ALIGN_512BYTES, 1024 => coff::IMAGE_SCN_ALIGN_1024BYTES, 2048 => coff::IMAGE_SCN_ALIGN_2048BYTES, 4096 => coff::IMAGE_SCN_ALIGN_4096BYTES, 8192 => coff::IMAGE_SCN_ALIGN_8192BYTES, _ => { return Err(Error(format!( "unimplemented section `{}` align {}", section.name().unwrap_or(""), section.align ))); } }; writer.write_section_header(writer::SectionHeader { name: section_offsets[index].name, size_of_raw_data: section.size as u32, pointer_to_raw_data: section_offsets[index].offset, pointer_to_relocations: section_offsets[index].reloc_offset, pointer_to_linenumbers: 0, number_of_relocations: section.relocations.len() as u32, number_of_linenumbers: 0, characteristics, }); } // Write section data and relocations. for section in &self.sections { writer.write_section(§ion.data); if !section.relocations.is_empty() { //debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len()); writer.write_relocations_count(section.relocations.len()); for reloc in §ion.relocations { let typ = if let RelocationFlags::Coff { typ } = reloc.flags { typ } else { return Err(Error("invalid relocation flags".into())); }; writer.write_relocation(writer::Relocation { virtual_address: reloc.offset as u32, symbol: symbol_offsets[reloc.symbol.0].index, typ, }); } } } // Write symbols. for (index, symbol) in self.symbols.iter().enumerate() { let section_number = match symbol.section { SymbolSection::None => { debug_assert_eq!(symbol.kind, SymbolKind::File); coff::IMAGE_SYM_DEBUG as u16 } SymbolSection::Undefined => coff::IMAGE_SYM_UNDEFINED as u16, SymbolSection::Absolute => coff::IMAGE_SYM_ABSOLUTE as u16, SymbolSection::Common => coff::IMAGE_SYM_UNDEFINED as u16, SymbolSection::Section(id) => id.0 as u16 + 1, }; let typ = if symbol.kind == SymbolKind::Text { coff::IMAGE_SYM_DTYPE_FUNCTION << coff::IMAGE_SYM_DTYPE_SHIFT } else { coff::IMAGE_SYM_TYPE_NULL }; let storage_class = match symbol.kind { SymbolKind::File => coff::IMAGE_SYM_CLASS_FILE, SymbolKind::Section => { if symbol.section.id().is_some() { coff::IMAGE_SYM_CLASS_STATIC } else { coff::IMAGE_SYM_CLASS_SECTION } } SymbolKind::Label => coff::IMAGE_SYM_CLASS_LABEL, SymbolKind::Text | SymbolKind::Data | SymbolKind::Tls => { match symbol.section { SymbolSection::None => { return Err(Error(format!( "missing section for symbol `{}`", symbol.name().unwrap_or("") ))); } SymbolSection::Undefined | SymbolSection::Common => { coff::IMAGE_SYM_CLASS_EXTERNAL } SymbolSection::Absolute | SymbolSection::Section(_) => { match symbol.scope { // TODO: does this need aux symbol records too? _ if symbol.weak => coff::IMAGE_SYM_CLASS_WEAK_EXTERNAL, SymbolScope::Unknown => { return Err(Error(format!( "unimplemented symbol `{}` scope {:?}", symbol.name().unwrap_or(""), symbol.scope ))); } SymbolScope::Compilation => coff::IMAGE_SYM_CLASS_STATIC, SymbolScope::Linkage | SymbolScope::Dynamic => { coff::IMAGE_SYM_CLASS_EXTERNAL } } } } } SymbolKind::Unknown => { return Err(Error(format!( "unimplemented symbol `{}` kind {:?}", symbol.name().unwrap_or(""), symbol.kind ))); } }; let number_of_aux_symbols = symbol_offsets[index].aux_count; let value = if symbol.section == SymbolSection::Common { symbol.size as u32 } else { symbol.value as u32 }; writer.write_symbol(writer::Symbol { name: symbol_offsets[index].name, value, section_number, typ, storage_class, number_of_aux_symbols, }); // Write auxiliary symbols. match symbol.kind { SymbolKind::File => { writer.write_aux_file_name(&symbol.name, number_of_aux_symbols); } SymbolKind::Section if symbol.section.id().is_some() => { debug_assert_eq!(number_of_aux_symbols, 1); let section_index = symbol.section.id().unwrap().0; let section = &self.sections[section_index]; writer.write_aux_section(writer::AuxSymbolSection { length: section.size as u32, number_of_relocations: section.relocations.len() as u32, number_of_linenumbers: 0, check_sum: if section.is_bss() { 0 } else { checksum(section.data()) }, number: section_offsets[section_index].associative_section, selection: section_offsets[section_index].selection, }); } _ => { debug_assert_eq!(number_of_aux_symbols, 0); } } } writer.write_strtab(); debug_assert_eq!(writer.reserved_len(), writer.len()); Ok(()) } } // JamCRC fn checksum(data: &[u8]) -> u32 { let mut hasher = crc32fast::Hasher::new_with_initial(0xffff_ffff); hasher.update(data); !hasher.finalize() } object-0.36.5/src/write/coff/writer.rs000064400000000000000000000422121046102023000157060ustar 00000000000000//! Helper for writing COFF files. use alloc::string::String; use alloc::vec::Vec; use core::mem; use crate::endian::{LittleEndian as LE, U16Bytes, U32Bytes, U16, U32}; use crate::pe; use crate::write::string::{StringId, StringTable}; use crate::write::util; use crate::write::{Error, Result, WritableBuffer}; /// A helper for writing COFF files. /// /// Writing uses a two phase approach. The first phase builds up all of the information /// that may need to be known ahead of time: /// - build string table /// - reserve section indices /// - reserve symbol indices /// - reserve file ranges for headers and sections /// /// Some of the information has ordering requirements. For example, strings must be added /// to the string table before reserving the file range for the string table. There are debug /// asserts to check some of these requirements. /// /// The second phase writes everything out in order. Thus the caller must ensure writing /// is in the same order that file ranges were reserved. There are debug asserts to assist /// with checking this. #[allow(missing_debug_implementations)] pub struct Writer<'a> { buffer: &'a mut dyn WritableBuffer, len: usize, section_num: u16, symtab_offset: u32, symtab_num: u32, strtab: StringTable<'a>, strtab_len: usize, strtab_offset: u32, strtab_data: Vec, } impl<'a> Writer<'a> { /// Create a new `Writer`. pub fn new(buffer: &'a mut dyn WritableBuffer) -> Self { Writer { buffer, len: 0, section_num: 0, symtab_offset: 0, symtab_num: 0, strtab: StringTable::default(), strtab_len: 0, strtab_offset: 0, strtab_data: Vec::new(), } } /// Return the current file length that has been reserved. pub fn reserved_len(&self) -> usize { self.len } /// Return the current file length that has been written. #[allow(clippy::len_without_is_empty)] pub fn len(&self) -> usize { self.buffer.len() } /// Reserve a file range with the given size and starting alignment. /// /// Returns the aligned offset of the start of the range. /// /// `align_start` must be a power of two. pub fn reserve(&mut self, len: usize, align_start: usize) -> u32 { if align_start > 1 { self.len = util::align(self.len, align_start); } let offset = self.len; self.len += len; offset as u32 } /// Write alignment padding bytes. pub fn write_align(&mut self, align_start: usize) { if align_start > 1 { util::write_align(self.buffer, align_start); } } /// Write data. pub fn write(&mut self, data: &[u8]) { self.buffer.write_bytes(data); } /// Reserve the file range up to the given file offset. pub fn reserve_until(&mut self, offset: usize) { debug_assert!(self.len <= offset); self.len = offset; } /// Write padding up to the given file offset. pub fn pad_until(&mut self, offset: usize) { debug_assert!(self.buffer.len() <= offset); self.buffer.resize(offset); } /// Reserve the range for the file header. /// /// This must be at the start of the file. pub fn reserve_file_header(&mut self) { debug_assert_eq!(self.len, 0); self.reserve(mem::size_of::(), 1); } /// Write the file header. /// /// This must be at the start of the file. /// /// Fields that can be derived from known information are automatically set by this function. pub fn write_file_header(&mut self, header: FileHeader) -> Result<()> { debug_assert_eq!(self.buffer.len(), 0); // Start writing. self.buffer .reserve(self.len) .map_err(|_| Error(String::from("Cannot allocate buffer")))?; // Write file header. let header = pe::ImageFileHeader { machine: U16::new(LE, header.machine), number_of_sections: U16::new(LE, self.section_num), time_date_stamp: U32::new(LE, header.time_date_stamp), pointer_to_symbol_table: U32::new(LE, self.symtab_offset), number_of_symbols: U32::new(LE, self.symtab_num), size_of_optional_header: U16::default(), characteristics: U16::new(LE, header.characteristics), }; self.buffer.write(&header); Ok(()) } /// Reserve the range for the section headers. pub fn reserve_section_headers(&mut self, section_num: u16) { debug_assert_eq!(self.section_num, 0); self.section_num = section_num; self.reserve( section_num as usize * mem::size_of::(), 1, ); } /// Write a section header. pub fn write_section_header(&mut self, section: SectionHeader) { let mut coff_section = pe::ImageSectionHeader { name: [0; 8], virtual_size: U32::default(), virtual_address: U32::default(), size_of_raw_data: U32::new(LE, section.size_of_raw_data), pointer_to_raw_data: U32::new(LE, section.pointer_to_raw_data), pointer_to_relocations: U32::new(LE, section.pointer_to_relocations), pointer_to_linenumbers: U32::new(LE, section.pointer_to_linenumbers), number_of_relocations: if section.number_of_relocations > 0xffff { U16::new(LE, 0xffff) } else { U16::new(LE, section.number_of_relocations as u16) }, number_of_linenumbers: U16::default(), characteristics: U32::new(LE, section.characteristics), }; match section.name { Name::Short(name) => coff_section.name = name, Name::Long(str_id) => { let mut str_offset = self.strtab.get_offset(str_id); if str_offset <= 9_999_999 { let mut name = [0; 7]; let mut len = 0; if str_offset == 0 { name[6] = b'0'; len = 1; } else { while str_offset != 0 { let rem = (str_offset % 10) as u8; str_offset /= 10; name[6 - len] = b'0' + rem; len += 1; } } coff_section.name = [0; 8]; coff_section.name[0] = b'/'; coff_section.name[1..][..len].copy_from_slice(&name[7 - len..]); } else { debug_assert!(str_offset as u64 <= 0xf_ffff_ffff); coff_section.name[0] = b'/'; coff_section.name[1] = b'/'; for i in 0..6 { let rem = (str_offset % 64) as u8; str_offset /= 64; let c = match rem { 0..=25 => b'A' + rem, 26..=51 => b'a' + rem - 26, 52..=61 => b'0' + rem - 52, 62 => b'+', 63 => b'/', _ => unreachable!(), }; coff_section.name[7 - i] = c; } } } } self.buffer.write(&coff_section); } /// Reserve the range for the section data. /// /// Returns the aligned offset of the start of the range. /// Does nothing and returns 0 if the length is zero. pub fn reserve_section(&mut self, len: usize) -> u32 { if len == 0 { return 0; } // TODO: not sure what alignment is required here, but this seems to match LLVM self.reserve(len, 4) } /// Write the alignment bytes prior to section data. /// /// This is unneeded if you are using `write_section` or `write_section_zeroes` /// for the data. pub fn write_section_align(&mut self) { util::write_align(self.buffer, 4); } /// Write the section data. /// /// Writes alignment bytes prior to the data. /// Does nothing if the data is empty. pub fn write_section(&mut self, data: &[u8]) { if data.is_empty() { return; } self.write_section_align(); self.buffer.write_bytes(data); } /// Write the section data using zero bytes. /// /// Writes alignment bytes prior to the data. /// Does nothing if the length is zero. pub fn write_section_zeroes(&mut self, len: usize) { if len == 0 { return; } self.write_section_align(); self.buffer.resize(self.buffer.len() + len); } /// Reserve a file range for the given number of relocations. /// /// This will automatically reserve an extra relocation if there are more than 0xffff. /// /// Returns the offset of the range. /// Does nothing and returns 0 if the count is zero. pub fn reserve_relocations(&mut self, mut count: usize) -> u32 { if count == 0 { return 0; } if count > 0xffff { count += 1; } self.reserve(count * mem::size_of::(), 1) } /// Write a relocation containing the count if required. /// /// This should be called before writing the first relocation for a section. pub fn write_relocations_count(&mut self, count: usize) { if count > 0xffff { let coff_relocation = pe::ImageRelocation { virtual_address: U32Bytes::new(LE, count as u32 + 1), symbol_table_index: U32Bytes::new(LE, 0), typ: U16Bytes::new(LE, 0), }; self.buffer.write(&coff_relocation); } } /// Write a relocation. pub fn write_relocation(&mut self, reloc: Relocation) { let coff_relocation = pe::ImageRelocation { virtual_address: U32Bytes::new(LE, reloc.virtual_address), symbol_table_index: U32Bytes::new(LE, reloc.symbol), typ: U16Bytes::new(LE, reloc.typ), }; self.buffer.write(&coff_relocation); } /// Reserve a symbol table entry. /// /// This must be called before [`Self::reserve_symtab_strtab`]. pub fn reserve_symbol_index(&mut self) -> u32 { debug_assert_eq!(self.symtab_offset, 0); let index = self.symtab_num; self.symtab_num += 1; index } /// Reserve a number of symbol table entries. pub fn reserve_symbol_indices(&mut self, count: u32) { debug_assert_eq!(self.symtab_offset, 0); self.symtab_num += count; } /// Write a symbol table entry. pub fn write_symbol(&mut self, symbol: Symbol) { let mut coff_symbol = pe::ImageSymbol { name: [0; 8], value: U32Bytes::new(LE, symbol.value), section_number: U16Bytes::new(LE, symbol.section_number), typ: U16Bytes::new(LE, symbol.typ), storage_class: symbol.storage_class, number_of_aux_symbols: symbol.number_of_aux_symbols, }; match symbol.name { Name::Short(name) => coff_symbol.name = name, Name::Long(str_id) => { let str_offset = self.strtab.get_offset(str_id); coff_symbol.name[4..8].copy_from_slice(&u32::to_le_bytes(str_offset as u32)); } } self.buffer.write(&coff_symbol); } /// Reserve auxiliary symbols for a file name. /// /// Returns the number of auxiliary symbols required. /// /// This must be called before [`Self::reserve_symtab_strtab`]. pub fn reserve_aux_file_name(&mut self, name: &[u8]) -> u8 { debug_assert_eq!(self.symtab_offset, 0); let aux_count = (name.len() + pe::IMAGE_SIZEOF_SYMBOL - 1) / pe::IMAGE_SIZEOF_SYMBOL; self.symtab_num += aux_count as u32; aux_count as u8 } /// Write auxiliary symbols for a file name. pub fn write_aux_file_name(&mut self, name: &[u8], aux_count: u8) { let aux_len = aux_count as usize * pe::IMAGE_SIZEOF_SYMBOL; debug_assert!(aux_len >= name.len()); let old_len = self.buffer.len(); self.buffer.write_bytes(name); self.buffer.resize(old_len + aux_len); } /// Reserve an auxiliary symbol for a section. /// /// Returns the number of auxiliary symbols required. /// /// This must be called before [`Self::reserve_symtab_strtab`]. pub fn reserve_aux_section(&mut self) -> u8 { debug_assert_eq!(self.symtab_offset, 0); self.symtab_num += 1; 1 } /// Write an auxiliary symbol for a section. pub fn write_aux_section(&mut self, section: AuxSymbolSection) { let aux = pe::ImageAuxSymbolSection { length: U32Bytes::new(LE, section.length), number_of_relocations: if section.number_of_relocations > 0xffff { U16Bytes::new(LE, 0xffff) } else { U16Bytes::new(LE, section.number_of_relocations as u16) }, number_of_linenumbers: U16Bytes::new(LE, section.number_of_linenumbers), check_sum: U32Bytes::new(LE, section.check_sum), number: U16Bytes::new(LE, section.number as u16), selection: section.selection, reserved: 0, high_number: U16Bytes::new(LE, (section.number >> 16) as u16), }; self.buffer.write(&aux); } /// Return the number of reserved symbol table entries. pub fn symbol_count(&self) -> u32 { self.symtab_num } /// Add a string to the string table. /// /// This must be called before [`Self::reserve_symtab_strtab`]. pub fn add_string(&mut self, name: &'a [u8]) -> StringId { debug_assert_eq!(self.strtab_offset, 0); self.strtab.add(name) } /// Add a section or symbol name to the string table if required. /// /// This must be called before [`Self::reserve_symtab_strtab`]. pub fn add_name(&mut self, name: &'a [u8]) -> Name { if name.len() > 8 { Name::Long(self.add_string(name)) } else { let mut short_name = [0; 8]; short_name[..name.len()].copy_from_slice(name); Name::Short(short_name) } } /// Reserve the range for the symbol table and string table. /// /// This must be called after functions that reserve symbol /// indices or add strings. pub fn reserve_symtab_strtab(&mut self) { debug_assert_eq!(self.symtab_offset, 0); self.symtab_offset = self.reserve(self.symtab_num as usize * pe::IMAGE_SIZEOF_SYMBOL, 1); debug_assert_eq!(self.strtab_offset, 0); // First 4 bytes of strtab are the length. self.strtab.write(4, &mut self.strtab_data); self.strtab_len = self.strtab_data.len() + 4; self.strtab_offset = self.reserve(self.strtab_len, 1); } /// Write the string table. pub fn write_strtab(&mut self) { debug_assert_eq!(self.strtab_offset, self.buffer.len() as u32); self.buffer .write_bytes(&u32::to_le_bytes(self.strtab_len as u32)); self.buffer.write_bytes(&self.strtab_data); } } /// Shortened and native endian version of [`pe::ImageFileHeader`]. #[allow(missing_docs)] #[derive(Debug, Default, Clone)] pub struct FileHeader { pub machine: u16, pub time_date_stamp: u32, pub characteristics: u16, } /// A section or symbol name. #[derive(Debug, Clone, Copy)] pub enum Name { /// An inline name. Short([u8; 8]), /// An id of a string table entry. Long(StringId), } impl Default for Name { fn default() -> Name { Name::Short([0; 8]) } } // From isn't useful. #[allow(clippy::from_over_into)] impl<'a> Into for &'a [u8; 8] { fn into(self) -> Name { Name::Short(*self) } } /// Native endian version of [`pe::ImageSectionHeader`]. #[allow(missing_docs)] #[derive(Debug, Default, Clone)] pub struct SectionHeader { pub name: Name, pub size_of_raw_data: u32, pub pointer_to_raw_data: u32, pub pointer_to_relocations: u32, pub pointer_to_linenumbers: u32, /// This will automatically be clamped if there are more than 0xffff. pub number_of_relocations: u32, pub number_of_linenumbers: u16, pub characteristics: u32, } /// Native endian version of [`pe::ImageSymbol`]. #[allow(missing_docs)] #[derive(Debug, Default, Clone)] pub struct Symbol { pub name: Name, pub value: u32, pub section_number: u16, pub typ: u16, pub storage_class: u8, pub number_of_aux_symbols: u8, } /// Native endian version of [`pe::ImageAuxSymbolSection`]. #[allow(missing_docs)] #[derive(Debug, Default, Clone)] pub struct AuxSymbolSection { pub length: u32, /// This will automatically be clamped if there are more than 0xffff. pub number_of_relocations: u32, pub number_of_linenumbers: u16, pub check_sum: u32, pub number: u32, pub selection: u8, } /// Native endian version of [`pe::ImageRelocation`]. #[allow(missing_docs)] #[derive(Debug, Default, Clone)] pub struct Relocation { pub virtual_address: u32, pub symbol: u32, pub typ: u16, } object-0.36.5/src/write/elf/mod.rs000064400000000000000000000003521046102023000150010ustar 00000000000000//! Support for writing ELF files. //! //! Provides [`Writer`] for low level writing of ELF files. //! This is also used to provide ELF support for [`write::Object`](crate::write::Object). mod object; mod writer; pub use writer::*; object-0.36.5/src/write/elf/object.rs000064400000000000000000001042241046102023000154730ustar 00000000000000use alloc::vec::Vec; use crate::write::elf::writer::*; use crate::write::string::StringId; use crate::write::*; use crate::{elf, pod}; #[derive(Clone, Copy)] struct ComdatOffsets { offset: usize, str_id: StringId, } #[derive(Clone, Copy)] struct SectionOffsets { index: SectionIndex, offset: usize, str_id: StringId, reloc_offset: usize, reloc_str_id: Option, } #[derive(Default, Clone, Copy)] struct SymbolOffsets { index: SymbolIndex, str_id: Option, } // Public methods. impl<'a> Object<'a> { /// Add a property with a u32 value to the ELF ".note.gnu.property" section. /// /// Requires `feature = "elf"`. pub fn add_elf_gnu_property_u32(&mut self, property: u32, value: u32) { if self.format != BinaryFormat::Elf { return; } let align = if self.elf_is_64() { 8 } else { 4 }; let mut data = Vec::with_capacity(32); let n_name = b"GNU\0"; data.extend_from_slice(pod::bytes_of(&elf::NoteHeader32 { n_namesz: U32::new(self.endian, n_name.len() as u32), n_descsz: U32::new(self.endian, util::align(3 * 4, align) as u32), n_type: U32::new(self.endian, elf::NT_GNU_PROPERTY_TYPE_0), })); data.extend_from_slice(n_name); // This happens to already be aligned correctly. debug_assert_eq!(util::align(data.len(), align), data.len()); data.extend_from_slice(pod::bytes_of(&U32::new(self.endian, property))); // Value size data.extend_from_slice(pod::bytes_of(&U32::new(self.endian, 4))); data.extend_from_slice(pod::bytes_of(&U32::new(self.endian, value))); util::write_align(&mut data, align); let section = self.section_id(StandardSection::GnuProperty); self.append_section_data(section, &data, align as u64); } } // Private methods. impl<'a> Object<'a> { pub(crate) fn elf_section_info( &self, section: StandardSection, ) -> (&'static [u8], &'static [u8], SectionKind, SectionFlags) { match section { StandardSection::Text => (&[], &b".text"[..], SectionKind::Text, SectionFlags::None), StandardSection::Data => (&[], &b".data"[..], SectionKind::Data, SectionFlags::None), StandardSection::ReadOnlyData | StandardSection::ReadOnlyString => ( &[], &b".rodata"[..], SectionKind::ReadOnlyData, SectionFlags::None, ), StandardSection::ReadOnlyDataWithRel => ( &[], b".data.rel.ro", SectionKind::ReadOnlyDataWithRel, SectionFlags::None, ), StandardSection::UninitializedData => ( &[], &b".bss"[..], SectionKind::UninitializedData, SectionFlags::None, ), StandardSection::Tls => (&[], &b".tdata"[..], SectionKind::Tls, SectionFlags::None), StandardSection::UninitializedTls => ( &[], &b".tbss"[..], SectionKind::UninitializedTls, SectionFlags::None, ), StandardSection::TlsVariables => { // Unsupported section. (&[], &[], SectionKind::TlsVariables, SectionFlags::None) } StandardSection::Common => { // Unsupported section. (&[], &[], SectionKind::Common, SectionFlags::None) } StandardSection::GnuProperty => ( &[], &b".note.gnu.property"[..], SectionKind::Note, SectionFlags::Elf { sh_flags: u64::from(elf::SHF_ALLOC), }, ), } } pub(crate) fn elf_subsection_name(&self, section: &[u8], value: &[u8]) -> Vec { let mut name = section.to_vec(); name.push(b'.'); name.extend_from_slice(value); name } fn elf_has_relocation_addend(&self) -> Result { Ok(match self.architecture { Architecture::Aarch64 => true, Architecture::Aarch64_Ilp32 => true, Architecture::Arm => false, Architecture::Avr => true, Architecture::Bpf => false, Architecture::Csky => true, Architecture::E2K32 => true, Architecture::E2K64 => true, Architecture::I386 => false, Architecture::X86_64 => true, Architecture::X86_64_X32 => true, Architecture::Hexagon => true, Architecture::LoongArch64 => true, Architecture::Mips => false, Architecture::Mips64 => true, Architecture::Msp430 => true, Architecture::PowerPc => true, Architecture::PowerPc64 => true, Architecture::Riscv64 => true, Architecture::Riscv32 => true, Architecture::S390x => true, Architecture::Sbf => false, Architecture::Sharc => true, Architecture::Sparc => true, Architecture::Sparc32Plus => true, Architecture::Sparc64 => true, Architecture::Xtensa => true, _ => { return Err(Error(format!( "unimplemented architecture {:?}", self.architecture ))); } }) } pub(crate) fn elf_translate_relocation(&mut self, reloc: &mut Relocation) -> Result<()> { use RelocationEncoding as E; use RelocationKind as K; let (kind, encoding, size) = if let RelocationFlags::Generic { kind, encoding, size, } = reloc.flags { (kind, encoding, size) } else { return Ok(()); }; let unsupported_reloc = || Err(Error(format!("unimplemented ELF relocation {:?}", reloc))); let r_type = match self.architecture { Architecture::Aarch64 => match (kind, encoding, size) { (K::Absolute, E::Generic, 64) => elf::R_AARCH64_ABS64, (K::Absolute, E::Generic, 32) => elf::R_AARCH64_ABS32, (K::Absolute, E::Generic, 16) => elf::R_AARCH64_ABS16, (K::Relative, E::Generic, 64) => elf::R_AARCH64_PREL64, (K::Relative, E::Generic, 32) => elf::R_AARCH64_PREL32, (K::Relative, E::Generic, 16) => elf::R_AARCH64_PREL16, (K::Relative, E::AArch64Call, 26) => elf::R_AARCH64_CALL26, (K::PltRelative, E::AArch64Call, 26) => elf::R_AARCH64_CALL26, _ => return unsupported_reloc(), }, Architecture::Aarch64_Ilp32 => match (kind, encoding, size) { (K::Absolute, E::Generic, 32) => elf::R_AARCH64_P32_ABS32, _ => return unsupported_reloc(), }, Architecture::Arm => match (kind, encoding, size) { (K::Absolute, _, 32) => elf::R_ARM_ABS32, _ => return unsupported_reloc(), }, Architecture::Avr => match (kind, encoding, size) { (K::Absolute, _, 32) => elf::R_AVR_32, (K::Absolute, _, 16) => elf::R_AVR_16, _ => return unsupported_reloc(), }, Architecture::Bpf => match (kind, encoding, size) { (K::Absolute, _, 64) => elf::R_BPF_64_64, (K::Absolute, _, 32) => elf::R_BPF_64_32, _ => return unsupported_reloc(), }, Architecture::Csky => match (kind, encoding, size) { (K::Absolute, _, 32) => elf::R_CKCORE_ADDR32, (K::Relative, E::Generic, 32) => elf::R_CKCORE_PCREL32, _ => return unsupported_reloc(), }, Architecture::I386 => match (kind, size) { (K::Absolute, 32) => elf::R_386_32, (K::Relative, 32) => elf::R_386_PC32, (K::Got, 32) => elf::R_386_GOT32, (K::PltRelative, 32) => elf::R_386_PLT32, (K::GotBaseOffset, 32) => elf::R_386_GOTOFF, (K::GotBaseRelative, 32) => elf::R_386_GOTPC, (K::Absolute, 16) => elf::R_386_16, (K::Relative, 16) => elf::R_386_PC16, (K::Absolute, 8) => elf::R_386_8, (K::Relative, 8) => elf::R_386_PC8, _ => return unsupported_reloc(), }, Architecture::E2K32 | Architecture::E2K64 => match (kind, encoding, size) { (K::Absolute, E::Generic, 32) => elf::R_E2K_32_ABS, (K::Absolute, E::E2KLit, 64) => elf::R_E2K_64_ABS_LIT, (K::Absolute, E::Generic, 64) => elf::R_E2K_64_ABS, (K::Relative, E::E2KDisp, 28) => elf::R_E2K_DISP, (K::Got, _, 32) => elf::R_E2K_GOT, _ => return unsupported_reloc(), }, Architecture::X86_64 | Architecture::X86_64_X32 => match (kind, encoding, size) { (K::Absolute, E::Generic, 64) => elf::R_X86_64_64, (K::Relative, E::X86Branch, 32) => elf::R_X86_64_PLT32, (K::Relative, _, 32) => elf::R_X86_64_PC32, (K::Got, _, 32) => elf::R_X86_64_GOT32, (K::PltRelative, _, 32) => elf::R_X86_64_PLT32, (K::GotRelative, _, 32) => elf::R_X86_64_GOTPCREL, (K::Absolute, E::Generic, 32) => elf::R_X86_64_32, (K::Absolute, E::X86Signed, 32) => elf::R_X86_64_32S, (K::Absolute, _, 16) => elf::R_X86_64_16, (K::Relative, _, 16) => elf::R_X86_64_PC16, (K::Absolute, _, 8) => elf::R_X86_64_8, (K::Relative, _, 8) => elf::R_X86_64_PC8, _ => return unsupported_reloc(), }, Architecture::Hexagon => match (kind, encoding, size) { (K::Absolute, _, 32) => elf::R_HEX_32, _ => return unsupported_reloc(), }, Architecture::LoongArch64 => match (kind, encoding, size) { (K::Absolute, _, 32) => elf::R_LARCH_32, (K::Absolute, _, 64) => elf::R_LARCH_64, (K::Relative, _, 32) => elf::R_LARCH_32_PCREL, (K::Relative, _, 64) => elf::R_LARCH_64_PCREL, (K::Relative, E::LoongArchBranch, 16) => elf::R_LARCH_B16, (K::PltRelative, E::LoongArchBranch, 16) => elf::R_LARCH_B16, (K::Relative, E::LoongArchBranch, 21) => elf::R_LARCH_B21, (K::PltRelative, E::LoongArchBranch, 21) => elf::R_LARCH_B21, (K::Relative, E::LoongArchBranch, 26) => elf::R_LARCH_B26, (K::PltRelative, E::LoongArchBranch, 26) => elf::R_LARCH_B26, _ => return unsupported_reloc(), }, Architecture::Mips | Architecture::Mips64 => match (kind, encoding, size) { (K::Absolute, _, 16) => elf::R_MIPS_16, (K::Absolute, _, 32) => elf::R_MIPS_32, (K::Absolute, _, 64) => elf::R_MIPS_64, _ => return unsupported_reloc(), }, Architecture::Msp430 => match (kind, encoding, size) { (K::Absolute, _, 32) => elf::R_MSP430_32, (K::Absolute, _, 16) => elf::R_MSP430_16_BYTE, _ => return unsupported_reloc(), }, Architecture::PowerPc => match (kind, encoding, size) { (K::Absolute, _, 32) => elf::R_PPC_ADDR32, _ => return unsupported_reloc(), }, Architecture::PowerPc64 => match (kind, encoding, size) { (K::Absolute, _, 32) => elf::R_PPC64_ADDR32, (K::Absolute, _, 64) => elf::R_PPC64_ADDR64, _ => return unsupported_reloc(), }, Architecture::Riscv32 | Architecture::Riscv64 => match (kind, encoding, size) { (K::Absolute, _, 32) => elf::R_RISCV_32, (K::Absolute, _, 64) => elf::R_RISCV_64, (K::Relative, E::Generic, 32) => elf::R_RISCV_32_PCREL, _ => return unsupported_reloc(), }, Architecture::S390x => match (kind, encoding, size) { (K::Absolute, E::Generic, 8) => elf::R_390_8, (K::Absolute, E::Generic, 16) => elf::R_390_16, (K::Absolute, E::Generic, 32) => elf::R_390_32, (K::Absolute, E::Generic, 64) => elf::R_390_64, (K::Relative, E::Generic, 16) => elf::R_390_PC16, (K::Relative, E::Generic, 32) => elf::R_390_PC32, (K::Relative, E::Generic, 64) => elf::R_390_PC64, (K::Relative, E::S390xDbl, 16) => elf::R_390_PC16DBL, (K::Relative, E::S390xDbl, 32) => elf::R_390_PC32DBL, (K::PltRelative, E::S390xDbl, 16) => elf::R_390_PLT16DBL, (K::PltRelative, E::S390xDbl, 32) => elf::R_390_PLT32DBL, (K::Got, E::Generic, 16) => elf::R_390_GOT16, (K::Got, E::Generic, 32) => elf::R_390_GOT32, (K::Got, E::Generic, 64) => elf::R_390_GOT64, (K::GotRelative, E::S390xDbl, 32) => elf::R_390_GOTENT, (K::GotBaseOffset, E::Generic, 16) => elf::R_390_GOTOFF16, (K::GotBaseOffset, E::Generic, 32) => elf::R_390_GOTOFF32, (K::GotBaseOffset, E::Generic, 64) => elf::R_390_GOTOFF64, (K::GotBaseRelative, E::Generic, 64) => elf::R_390_GOTPC, (K::GotBaseRelative, E::S390xDbl, 32) => elf::R_390_GOTPCDBL, _ => return unsupported_reloc(), }, Architecture::Sbf => match (kind, encoding, size) { (K::Absolute, _, 64) => elf::R_SBF_64_64, (K::Absolute, _, 32) => elf::R_SBF_64_32, _ => return unsupported_reloc(), }, Architecture::Sharc => match (kind, encoding, size) { (K::Absolute, E::SharcTypeA, 32) => elf::R_SHARC_ADDR32_V3, (K::Absolute, E::Generic, 32) => elf::R_SHARC_ADDR_VAR_V3, (K::Relative, E::SharcTypeA, 24) => elf::R_SHARC_PCRLONG_V3, (K::Relative, E::SharcTypeA, 6) => elf::R_SHARC_PCRSHORT_V3, (K::Relative, E::SharcTypeB, 6) => elf::R_SHARC_PCRSHORT_V3, (K::Absolute, E::Generic, 16) => elf::R_SHARC_ADDR_VAR16_V3, (K::Absolute, E::SharcTypeA, 16) => elf::R_SHARC_DATA16_V3, (K::Absolute, E::SharcTypeB, 16) => elf::R_SHARC_DATA16_VISA_V3, (K::Absolute, E::SharcTypeA, 24) => elf::R_SHARC_ADDR24_V3, (K::Absolute, E::SharcTypeA, 6) => elf::R_SHARC_DATA6_V3, (K::Absolute, E::SharcTypeB, 6) => elf::R_SHARC_DATA6_VISA_V3, (K::Absolute, E::SharcTypeB, 7) => elf::R_SHARC_DATA7_VISA_V3, _ => return unsupported_reloc(), }, Architecture::Sparc | Architecture::Sparc32Plus => match (kind, encoding, size) { // TODO: use R_SPARC_32 if aligned. (K::Absolute, _, 32) => elf::R_SPARC_UA32, _ => return unsupported_reloc(), }, Architecture::Sparc64 => match (kind, encoding, size) { // TODO: use R_SPARC_32/R_SPARC_64 if aligned. (K::Absolute, _, 32) => elf::R_SPARC_UA32, (K::Absolute, _, 64) => elf::R_SPARC_UA64, _ => return unsupported_reloc(), }, Architecture::Xtensa => match (kind, encoding, size) { (K::Absolute, _, 32) => elf::R_XTENSA_32, (K::Relative, E::Generic, 32) => elf::R_XTENSA_32_PCREL, _ => return unsupported_reloc(), }, _ => { return Err(Error(format!( "unimplemented architecture {:?}", self.architecture ))); } }; reloc.flags = RelocationFlags::Elf { r_type }; Ok(()) } pub(crate) fn elf_adjust_addend(&mut self, _relocation: &mut Relocation) -> Result { // Determine whether the addend is stored in the relocation or the data. let implicit = !self.elf_has_relocation_addend()?; Ok(implicit) } pub(crate) fn elf_relocation_size(&self, reloc: &Relocation) -> Result { let r_type = if let RelocationFlags::Elf { r_type } = reloc.flags { r_type } else { return Err(Error("invalid relocation flags".into())); }; // This only needs to support architectures that use implicit addends. let size = match self.architecture { Architecture::Arm => match r_type { elf::R_ARM_ABS16 => Some(16), elf::R_ARM_ABS32 | elf::R_ARM_REL32 => Some(32), _ => None, }, Architecture::Bpf => match r_type { elf::R_BPF_64_32 => Some(32), elf::R_BPF_64_64 => Some(64), _ => None, }, Architecture::I386 => match r_type { elf::R_386_8 | elf::R_386_PC8 => Some(8), elf::R_386_16 | elf::R_386_PC16 => Some(16), elf::R_386_32 | elf::R_386_PC32 | elf::R_386_GOT32 | elf::R_386_PLT32 | elf::R_386_GOTOFF | elf::R_386_GOTPC => Some(32), _ => None, }, Architecture::Mips => match r_type { elf::R_MIPS_16 => Some(16), elf::R_MIPS_32 => Some(32), elf::R_MIPS_64 => Some(64), _ => None, }, Architecture::Sbf => match r_type { elf::R_SBF_64_32 => Some(32), elf::R_SBF_64_64 => Some(64), _ => None, }, _ => { return Err(Error(format!( "unimplemented architecture {:?}", self.architecture ))); } }; size.ok_or_else(|| Error(format!("unsupported relocation for size {:?}", reloc))) } pub(crate) fn elf_is_64(&self) -> bool { match self.architecture.address_size().unwrap() { AddressSize::U8 | AddressSize::U16 | AddressSize::U32 => false, AddressSize::U64 => true, } } pub(crate) fn elf_write(&self, buffer: &mut dyn WritableBuffer) -> Result<()> { // Create reloc section header names so we can reference them. let is_rela = self.elf_has_relocation_addend()?; let reloc_names: Vec<_> = self .sections .iter() .map(|section| { let mut reloc_name = Vec::with_capacity( if is_rela { ".rela".len() } else { ".rel".len() } + section.name.len(), ); if !section.relocations.is_empty() { reloc_name.extend_from_slice(if is_rela { &b".rela"[..] } else { &b".rel"[..] }); reloc_name.extend_from_slice(§ion.name); } reloc_name }) .collect(); // Start calculating offsets of everything. let mut writer = Writer::new(self.endian, self.elf_is_64(), buffer); writer.reserve_file_header(); // Calculate size of section data. let mut comdat_offsets = Vec::with_capacity(self.comdats.len()); for comdat in &self.comdats { if comdat.kind != ComdatKind::Any { return Err(Error(format!( "unsupported COMDAT symbol `{}` kind {:?}", self.symbols[comdat.symbol.0].name().unwrap_or(""), comdat.kind ))); } writer.reserve_section_index(); let offset = writer.reserve_comdat(comdat.sections.len()); let str_id = writer.add_section_name(b".group"); comdat_offsets.push(ComdatOffsets { offset, str_id }); } let mut section_offsets = Vec::with_capacity(self.sections.len()); for (section, reloc_name) in self.sections.iter().zip(reloc_names.iter()) { let index = writer.reserve_section_index(); let offset = writer.reserve(section.data.len(), section.align as usize); let str_id = writer.add_section_name(§ion.name); let mut reloc_str_id = None; if !section.relocations.is_empty() { writer.reserve_section_index(); reloc_str_id = Some(writer.add_section_name(reloc_name)); } section_offsets.push(SectionOffsets { index, offset, str_id, // Relocation data is reserved later. reloc_offset: 0, reloc_str_id, }); } // Calculate index of symbols and add symbol strings to strtab. let mut symbol_offsets = vec![SymbolOffsets::default(); self.symbols.len()]; writer.reserve_null_symbol_index(); // Local symbols must come before global. for (index, symbol) in self.symbols.iter().enumerate() { if symbol.is_local() { let section_index = symbol.section.id().map(|s| section_offsets[s.0].index); symbol_offsets[index].index = writer.reserve_symbol_index(section_index); } } let symtab_num_local = writer.symbol_count(); for (index, symbol) in self.symbols.iter().enumerate() { if !symbol.is_local() { let section_index = symbol.section.id().map(|s| section_offsets[s.0].index); symbol_offsets[index].index = writer.reserve_symbol_index(section_index); } } for (index, symbol) in self.symbols.iter().enumerate() { if symbol.kind != SymbolKind::Section && !symbol.name.is_empty() { symbol_offsets[index].str_id = Some(writer.add_string(&symbol.name)); } } // Calculate size of symbols. writer.reserve_symtab_section_index(); writer.reserve_symtab(); if writer.symtab_shndx_needed() { writer.reserve_symtab_shndx_section_index(); } writer.reserve_symtab_shndx(); writer.reserve_strtab_section_index(); writer.reserve_strtab(); // Calculate size of relocations. for (index, section) in self.sections.iter().enumerate() { let count = section.relocations.len(); if count != 0 { section_offsets[index].reloc_offset = writer.reserve_relocations(count, is_rela); } } // Calculate size of section headers. writer.reserve_shstrtab_section_index(); writer.reserve_shstrtab(); writer.reserve_section_headers(); // Start writing. let e_type = elf::ET_REL; let e_machine = match (self.architecture, self.sub_architecture) { (Architecture::Aarch64, None) => elf::EM_AARCH64, (Architecture::Aarch64_Ilp32, None) => elf::EM_AARCH64, (Architecture::Arm, None) => elf::EM_ARM, (Architecture::Avr, None) => elf::EM_AVR, (Architecture::Bpf, None) => elf::EM_BPF, (Architecture::Csky, None) => elf::EM_CSKY, (Architecture::E2K32, None) => elf::EM_MCST_ELBRUS, (Architecture::E2K64, None) => elf::EM_MCST_ELBRUS, (Architecture::I386, None) => elf::EM_386, (Architecture::X86_64, None) => elf::EM_X86_64, (Architecture::X86_64_X32, None) => elf::EM_X86_64, (Architecture::Hexagon, None) => elf::EM_HEXAGON, (Architecture::LoongArch64, None) => elf::EM_LOONGARCH, (Architecture::Mips, None) => elf::EM_MIPS, (Architecture::Mips64, None) => elf::EM_MIPS, (Architecture::Msp430, None) => elf::EM_MSP430, (Architecture::PowerPc, None) => elf::EM_PPC, (Architecture::PowerPc64, None) => elf::EM_PPC64, (Architecture::Riscv32, None) => elf::EM_RISCV, (Architecture::Riscv64, None) => elf::EM_RISCV, (Architecture::S390x, None) => elf::EM_S390, (Architecture::Sbf, None) => elf::EM_SBF, (Architecture::Sharc, None) => elf::EM_SHARC, (Architecture::Sparc, None) => elf::EM_SPARC, (Architecture::Sparc32Plus, None) => elf::EM_SPARC32PLUS, (Architecture::Sparc64, None) => elf::EM_SPARCV9, (Architecture::Xtensa, None) => elf::EM_XTENSA, _ => { return Err(Error(format!( "unimplemented architecture {:?} with sub-architecture {:?}", self.architecture, self.sub_architecture ))); } }; let (os_abi, abi_version, e_flags) = if let FileFlags::Elf { os_abi, abi_version, e_flags, } = self.flags { (os_abi, abi_version, e_flags) } else { (elf::ELFOSABI_NONE, 0, 0) }; writer.write_file_header(&FileHeader { os_abi, abi_version, e_type, e_machine, e_entry: 0, e_flags, })?; // Write section data. for comdat in &self.comdats { writer.write_comdat_header(); for section in &comdat.sections { writer.write_comdat_entry(section_offsets[section.0].index); } } for (index, section) in self.sections.iter().enumerate() { writer.write_align(section.align as usize); debug_assert_eq!(section_offsets[index].offset, writer.len()); writer.write(§ion.data); } // Write symbols. writer.write_null_symbol(); let mut write_symbol = |index: usize, symbol: &Symbol| -> Result<()> { let st_info = if let SymbolFlags::Elf { st_info, .. } = symbol.flags { st_info } else { let st_type = match symbol.kind { SymbolKind::Text => { if symbol.is_undefined() { elf::STT_NOTYPE } else { elf::STT_FUNC } } SymbolKind::Data => { if symbol.is_undefined() { elf::STT_NOTYPE } else if symbol.is_common() { elf::STT_COMMON } else { elf::STT_OBJECT } } SymbolKind::Section => elf::STT_SECTION, SymbolKind::File => elf::STT_FILE, SymbolKind::Tls => elf::STT_TLS, SymbolKind::Label => elf::STT_NOTYPE, SymbolKind::Unknown => { if symbol.is_undefined() { elf::STT_NOTYPE } else { return Err(Error(format!( "unimplemented symbol `{}` kind {:?}", symbol.name().unwrap_or(""), symbol.kind ))); } } }; let st_bind = if symbol.weak { elf::STB_WEAK } else if symbol.is_undefined() { elf::STB_GLOBAL } else if symbol.is_local() { elf::STB_LOCAL } else { elf::STB_GLOBAL }; (st_bind << 4) + st_type }; let st_other = if let SymbolFlags::Elf { st_other, .. } = symbol.flags { st_other } else if symbol.scope == SymbolScope::Linkage { elf::STV_HIDDEN } else { elf::STV_DEFAULT }; let (st_shndx, section) = match symbol.section { SymbolSection::None => { debug_assert_eq!(symbol.kind, SymbolKind::File); (elf::SHN_ABS, None) } SymbolSection::Undefined => (elf::SHN_UNDEF, None), SymbolSection::Absolute => (elf::SHN_ABS, None), SymbolSection::Common => (elf::SHN_COMMON, None), SymbolSection::Section(id) => (0, Some(section_offsets[id.0].index)), }; writer.write_symbol(&Sym { name: symbol_offsets[index].str_id, section, st_info, st_other, st_shndx, st_value: symbol.value, st_size: symbol.size, }); Ok(()) }; for (index, symbol) in self.symbols.iter().enumerate() { if symbol.is_local() { write_symbol(index, symbol)?; } } for (index, symbol) in self.symbols.iter().enumerate() { if !symbol.is_local() { write_symbol(index, symbol)?; } } writer.write_symtab_shndx(); writer.write_strtab(); // Write relocations. for (index, section) in self.sections.iter().enumerate() { if !section.relocations.is_empty() { writer.write_align_relocation(); debug_assert_eq!(section_offsets[index].reloc_offset, writer.len()); for reloc in §ion.relocations { let r_type = if let RelocationFlags::Elf { r_type } = reloc.flags { r_type } else { return Err(Error("invalid relocation flags".into())); }; let r_sym = symbol_offsets[reloc.symbol.0].index.0; writer.write_relocation( is_rela, &Rel { r_offset: reloc.offset, r_sym, r_type, r_addend: reloc.addend, }, ); } } } writer.write_shstrtab(); // Write section headers. writer.write_null_section_header(); let symtab_index = writer.symtab_index(); for (comdat, comdat_offset) in self.comdats.iter().zip(comdat_offsets.iter()) { writer.write_comdat_section_header( comdat_offset.str_id, symtab_index, symbol_offsets[comdat.symbol.0].index, comdat_offset.offset, comdat.sections.len(), ); } for (index, section) in self.sections.iter().enumerate() { let sh_type = match section.kind { SectionKind::UninitializedData | SectionKind::UninitializedTls => elf::SHT_NOBITS, SectionKind::Note => elf::SHT_NOTE, SectionKind::Elf(sh_type) => sh_type, _ => elf::SHT_PROGBITS, }; let sh_flags = if let SectionFlags::Elf { sh_flags } = section.flags { sh_flags } else { match section.kind { SectionKind::Text => elf::SHF_ALLOC | elf::SHF_EXECINSTR, SectionKind::Data | SectionKind::ReadOnlyDataWithRel => { elf::SHF_ALLOC | elf::SHF_WRITE } SectionKind::Tls => elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_TLS, SectionKind::UninitializedData => elf::SHF_ALLOC | elf::SHF_WRITE, SectionKind::UninitializedTls => elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_TLS, SectionKind::ReadOnlyData => elf::SHF_ALLOC, SectionKind::ReadOnlyString => { elf::SHF_ALLOC | elf::SHF_STRINGS | elf::SHF_MERGE } SectionKind::OtherString | SectionKind::DebugString => { elf::SHF_STRINGS | elf::SHF_MERGE } SectionKind::Other | SectionKind::Debug | SectionKind::Metadata | SectionKind::Linker | SectionKind::Note | SectionKind::Elf(_) => 0, SectionKind::Unknown | SectionKind::Common | SectionKind::TlsVariables => { return Err(Error(format!( "unimplemented section `{}` kind {:?}", section.name().unwrap_or(""), section.kind ))); } } .into() }; // TODO: not sure if this is correct, maybe user should determine this let sh_entsize = match section.kind { SectionKind::ReadOnlyString | SectionKind::OtherString => 1, _ => 0, }; writer.write_section_header(&SectionHeader { name: Some(section_offsets[index].str_id), sh_type, sh_flags, sh_addr: 0, sh_offset: section_offsets[index].offset as u64, sh_size: section.size, sh_link: 0, sh_info: 0, sh_addralign: section.align, sh_entsize, }); if !section.relocations.is_empty() { writer.write_relocation_section_header( section_offsets[index].reloc_str_id.unwrap(), section_offsets[index].index, symtab_index, section_offsets[index].reloc_offset, section.relocations.len(), is_rela, ); } } writer.write_symtab_section_header(symtab_num_local); writer.write_symtab_shndx_section_header(); writer.write_strtab_section_header(); writer.write_shstrtab_section_header(); debug_assert_eq!(writer.reserved_len(), writer.len()); Ok(()) } } object-0.36.5/src/write/elf/writer.rs000064400000000000000000002434251046102023000155500ustar 00000000000000//! Helper for writing ELF files. use alloc::string::String; use alloc::vec::Vec; use core::mem; use crate::elf; use crate::endian::*; use crate::pod; use crate::write::string::{StringId, StringTable}; use crate::write::util; use crate::write::{Error, Result, WritableBuffer}; const ALIGN_SYMTAB_SHNDX: usize = 4; const ALIGN_HASH: usize = 4; const ALIGN_GNU_VERSYM: usize = 2; const ALIGN_GNU_VERDEF: usize = 4; const ALIGN_GNU_VERNEED: usize = 4; /// The index of an ELF section. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SectionIndex(pub u32); /// The index of an ELF symbol. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SymbolIndex(pub u32); /// A helper for writing ELF files. /// /// Writing uses a two phase approach. The first phase builds up all of the information /// that may need to be known ahead of time: /// - build string tables /// - reserve section indices /// - reserve symbol indices /// - reserve file ranges for headers and sections /// /// Some of the information has ordering requirements. For example, strings must be added /// to string tables before reserving the file range for the string table. Symbol indices /// must be reserved after reserving the section indices they reference. There are debug /// asserts to check some of these requirements. /// /// The second phase writes everything out in order. Thus the caller must ensure writing /// is in the same order that file ranges were reserved. There are debug asserts to assist /// with checking this. #[allow(missing_debug_implementations)] pub struct Writer<'a> { endian: Endianness, is_64: bool, is_mips64el: bool, elf_align: usize, buffer: &'a mut dyn WritableBuffer, len: usize, segment_offset: usize, segment_num: u32, section_offset: usize, section_num: u32, shstrtab: StringTable<'a>, shstrtab_str_id: Option, shstrtab_index: SectionIndex, shstrtab_offset: usize, shstrtab_data: Vec, need_strtab: bool, strtab: StringTable<'a>, strtab_str_id: Option, strtab_index: SectionIndex, strtab_offset: usize, strtab_data: Vec, symtab_str_id: Option, symtab_index: SectionIndex, symtab_offset: usize, symtab_num: u32, need_symtab_shndx: bool, symtab_shndx_str_id: Option, symtab_shndx_offset: usize, symtab_shndx_data: Vec, need_dynstr: bool, dynstr: StringTable<'a>, dynstr_str_id: Option, dynstr_index: SectionIndex, dynstr_offset: usize, dynstr_data: Vec, dynsym_str_id: Option, dynsym_index: SectionIndex, dynsym_offset: usize, dynsym_num: u32, dynamic_str_id: Option, dynamic_offset: usize, dynamic_num: usize, hash_str_id: Option, hash_offset: usize, hash_size: usize, gnu_hash_str_id: Option, gnu_hash_offset: usize, gnu_hash_size: usize, gnu_versym_str_id: Option, gnu_versym_offset: usize, gnu_verdef_str_id: Option, gnu_verdef_offset: usize, gnu_verdef_size: usize, gnu_verdef_count: u16, gnu_verdef_remaining: u16, gnu_verdaux_remaining: u16, gnu_verneed_str_id: Option, gnu_verneed_offset: usize, gnu_verneed_size: usize, gnu_verneed_count: u16, gnu_verneed_remaining: u16, gnu_vernaux_remaining: u16, gnu_attributes_str_id: Option, gnu_attributes_offset: usize, gnu_attributes_size: usize, } impl<'a> Writer<'a> { /// Create a new `Writer` for the given endianness and ELF class. pub fn new(endian: Endianness, is_64: bool, buffer: &'a mut dyn WritableBuffer) -> Self { let elf_align = if is_64 { 8 } else { 4 }; Writer { endian, is_64, // Determined later. is_mips64el: false, elf_align, buffer, len: 0, segment_offset: 0, segment_num: 0, section_offset: 0, section_num: 0, shstrtab: StringTable::default(), shstrtab_str_id: None, shstrtab_index: SectionIndex(0), shstrtab_offset: 0, shstrtab_data: Vec::new(), need_strtab: false, strtab: StringTable::default(), strtab_str_id: None, strtab_index: SectionIndex(0), strtab_offset: 0, strtab_data: Vec::new(), symtab_str_id: None, symtab_index: SectionIndex(0), symtab_offset: 0, symtab_num: 0, need_symtab_shndx: false, symtab_shndx_str_id: None, symtab_shndx_offset: 0, symtab_shndx_data: Vec::new(), need_dynstr: false, dynstr: StringTable::default(), dynstr_str_id: None, dynstr_index: SectionIndex(0), dynstr_offset: 0, dynstr_data: Vec::new(), dynsym_str_id: None, dynsym_index: SectionIndex(0), dynsym_offset: 0, dynsym_num: 0, dynamic_str_id: None, dynamic_offset: 0, dynamic_num: 0, hash_str_id: None, hash_offset: 0, hash_size: 0, gnu_hash_str_id: None, gnu_hash_offset: 0, gnu_hash_size: 0, gnu_versym_str_id: None, gnu_versym_offset: 0, gnu_verdef_str_id: None, gnu_verdef_offset: 0, gnu_verdef_size: 0, gnu_verdef_count: 0, gnu_verdef_remaining: 0, gnu_verdaux_remaining: 0, gnu_verneed_str_id: None, gnu_verneed_offset: 0, gnu_verneed_size: 0, gnu_verneed_count: 0, gnu_verneed_remaining: 0, gnu_vernaux_remaining: 0, gnu_attributes_str_id: None, gnu_attributes_offset: 0, gnu_attributes_size: 0, } } /// Get the file class that will be written. fn class(&self) -> Class { Class { is_64: self.is_64 } } /// Return the current file length that has been reserved. pub fn reserved_len(&self) -> usize { self.len } /// Return the current file length that has been written. #[allow(clippy::len_without_is_empty)] pub fn len(&self) -> usize { self.buffer.len() } /// Reserve a file range with the given size and starting alignment. /// /// Returns the aligned offset of the start of the range. /// /// `align_start` must be a power of two. pub fn reserve(&mut self, len: usize, align_start: usize) -> usize { if align_start > 1 { self.len = util::align(self.len, align_start); } let offset = self.len; self.len += len; offset } /// Write alignment padding bytes. pub fn write_align(&mut self, align_start: usize) { if align_start > 1 { util::write_align(self.buffer, align_start); } } /// Write data. /// /// This is typically used to write section data. pub fn write(&mut self, data: &[u8]) { self.buffer.write_bytes(data); } /// Reserve the file range up to the given file offset. pub fn reserve_until(&mut self, offset: usize) { debug_assert!(self.len <= offset); self.len = offset; } /// Write padding up to the given file offset. pub fn pad_until(&mut self, offset: usize) { debug_assert!(self.buffer.len() <= offset); self.buffer.resize(offset); } /// Reserve the range for the file header. /// /// This must be at the start of the file. pub fn reserve_file_header(&mut self) { debug_assert_eq!(self.len, 0); self.reserve(self.class().file_header_size(), 1); } /// Write the file header. /// /// This must be at the start of the file. /// /// Fields that can be derived from known information are automatically set by this function. pub fn write_file_header(&mut self, header: &FileHeader) -> Result<()> { debug_assert_eq!(self.buffer.len(), 0); self.is_mips64el = self.is_64 && self.endian.is_little_endian() && header.e_machine == elf::EM_MIPS; // Start writing. self.buffer .reserve(self.len) .map_err(|_| Error(String::from("Cannot allocate buffer")))?; // Write file header. let e_ident = elf::Ident { magic: elf::ELFMAG, class: if self.is_64 { elf::ELFCLASS64 } else { elf::ELFCLASS32 }, data: if self.endian.is_little_endian() { elf::ELFDATA2LSB } else { elf::ELFDATA2MSB }, version: elf::EV_CURRENT, os_abi: header.os_abi, abi_version: header.abi_version, padding: [0; 7], }; let e_ehsize = self.class().file_header_size() as u16; let e_phoff = self.segment_offset as u64; let e_phentsize = if self.segment_num == 0 { 0 } else { self.class().program_header_size() as u16 }; // TODO: overflow let e_phnum = self.segment_num as u16; let e_shoff = self.section_offset as u64; let e_shentsize = if self.section_num == 0 { 0 } else { self.class().section_header_size() as u16 }; let e_shnum = if self.section_num >= elf::SHN_LORESERVE.into() { 0 } else { self.section_num as u16 }; let e_shstrndx = if self.shstrtab_index.0 >= elf::SHN_LORESERVE.into() { elf::SHN_XINDEX } else { self.shstrtab_index.0 as u16 }; let endian = self.endian; if self.is_64 { let file = elf::FileHeader64 { e_ident, e_type: U16::new(endian, header.e_type), e_machine: U16::new(endian, header.e_machine), e_version: U32::new(endian, elf::EV_CURRENT.into()), e_entry: U64::new(endian, header.e_entry), e_phoff: U64::new(endian, e_phoff), e_shoff: U64::new(endian, e_shoff), e_flags: U32::new(endian, header.e_flags), e_ehsize: U16::new(endian, e_ehsize), e_phentsize: U16::new(endian, e_phentsize), e_phnum: U16::new(endian, e_phnum), e_shentsize: U16::new(endian, e_shentsize), e_shnum: U16::new(endian, e_shnum), e_shstrndx: U16::new(endian, e_shstrndx), }; self.buffer.write(&file) } else { let file = elf::FileHeader32 { e_ident, e_type: U16::new(endian, header.e_type), e_machine: U16::new(endian, header.e_machine), e_version: U32::new(endian, elf::EV_CURRENT.into()), e_entry: U32::new(endian, header.e_entry as u32), e_phoff: U32::new(endian, e_phoff as u32), e_shoff: U32::new(endian, e_shoff as u32), e_flags: U32::new(endian, header.e_flags), e_ehsize: U16::new(endian, e_ehsize), e_phentsize: U16::new(endian, e_phentsize), e_phnum: U16::new(endian, e_phnum), e_shentsize: U16::new(endian, e_shentsize), e_shnum: U16::new(endian, e_shnum), e_shstrndx: U16::new(endian, e_shstrndx), }; self.buffer.write(&file); } Ok(()) } /// Reserve the range for the program headers. pub fn reserve_program_headers(&mut self, num: u32) { debug_assert_eq!(self.segment_offset, 0); if num == 0 { return; } self.segment_num = num; self.segment_offset = self.reserve( num as usize * self.class().program_header_size(), self.elf_align, ); } /// Write alignment padding bytes prior to the program headers. pub fn write_align_program_headers(&mut self) { if self.segment_offset == 0 { return; } util::write_align(self.buffer, self.elf_align); debug_assert_eq!(self.segment_offset, self.buffer.len()); } /// Write a program header. pub fn write_program_header(&mut self, header: &ProgramHeader) { let endian = self.endian; if self.is_64 { let header = elf::ProgramHeader64 { p_type: U32::new(endian, header.p_type), p_flags: U32::new(endian, header.p_flags), p_offset: U64::new(endian, header.p_offset), p_vaddr: U64::new(endian, header.p_vaddr), p_paddr: U64::new(endian, header.p_paddr), p_filesz: U64::new(endian, header.p_filesz), p_memsz: U64::new(endian, header.p_memsz), p_align: U64::new(endian, header.p_align), }; self.buffer.write(&header); } else { let header = elf::ProgramHeader32 { p_type: U32::new(endian, header.p_type), p_offset: U32::new(endian, header.p_offset as u32), p_vaddr: U32::new(endian, header.p_vaddr as u32), p_paddr: U32::new(endian, header.p_paddr as u32), p_filesz: U32::new(endian, header.p_filesz as u32), p_memsz: U32::new(endian, header.p_memsz as u32), p_flags: U32::new(endian, header.p_flags), p_align: U32::new(endian, header.p_align as u32), }; self.buffer.write(&header); } } /// Reserve the section index for the null section header. /// /// The null section header is usually automatically reserved, /// but this can be used to force an empty section table. /// /// This must be called before [`Self::reserve_section_headers`]. pub fn reserve_null_section_index(&mut self) -> SectionIndex { debug_assert_eq!(self.section_num, 0); if self.section_num == 0 { self.section_num = 1; } SectionIndex(0) } /// Reserve a section table index. /// /// Automatically also reserves the null section header if required. /// /// This must be called before [`Self::reserve_section_headers`]. pub fn reserve_section_index(&mut self) -> SectionIndex { debug_assert_eq!(self.section_offset, 0); if self.section_num == 0 { self.section_num = 1; } let index = self.section_num; self.section_num += 1; SectionIndex(index) } /// Reserve the range for the section headers. /// /// This function does nothing if no sections were reserved. /// This must be called after [`Self::reserve_section_index`] /// and other functions that reserve section indices. pub fn reserve_section_headers(&mut self) { debug_assert_eq!(self.section_offset, 0); if self.section_num == 0 { return; } self.section_offset = self.reserve( self.section_num as usize * self.class().section_header_size(), self.elf_align, ); } /// Write the null section header. /// /// This must be the first section header that is written. /// This function does nothing if no sections were reserved. pub fn write_null_section_header(&mut self) { if self.section_num == 0 { return; } util::write_align(self.buffer, self.elf_align); debug_assert_eq!(self.section_offset, self.buffer.len()); self.write_section_header(&SectionHeader { name: None, sh_type: 0, sh_flags: 0, sh_addr: 0, sh_offset: 0, sh_size: if self.section_num >= elf::SHN_LORESERVE.into() { self.section_num.into() } else { 0 }, sh_link: if self.shstrtab_index.0 >= elf::SHN_LORESERVE.into() { self.shstrtab_index.0 } else { 0 }, // TODO: e_phnum overflow sh_info: 0, sh_addralign: 0, sh_entsize: 0, }); } /// Write a section header. pub fn write_section_header(&mut self, section: &SectionHeader) { let sh_name = if let Some(name) = section.name { self.shstrtab.get_offset(name) as u32 } else { 0 }; let endian = self.endian; if self.is_64 { let section = elf::SectionHeader64 { sh_name: U32::new(endian, sh_name), sh_type: U32::new(endian, section.sh_type), sh_flags: U64::new(endian, section.sh_flags), sh_addr: U64::new(endian, section.sh_addr), sh_offset: U64::new(endian, section.sh_offset), sh_size: U64::new(endian, section.sh_size), sh_link: U32::new(endian, section.sh_link), sh_info: U32::new(endian, section.sh_info), sh_addralign: U64::new(endian, section.sh_addralign), sh_entsize: U64::new(endian, section.sh_entsize), }; self.buffer.write(§ion); } else { let section = elf::SectionHeader32 { sh_name: U32::new(endian, sh_name), sh_type: U32::new(endian, section.sh_type), sh_flags: U32::new(endian, section.sh_flags as u32), sh_addr: U32::new(endian, section.sh_addr as u32), sh_offset: U32::new(endian, section.sh_offset as u32), sh_size: U32::new(endian, section.sh_size as u32), sh_link: U32::new(endian, section.sh_link), sh_info: U32::new(endian, section.sh_info), sh_addralign: U32::new(endian, section.sh_addralign as u32), sh_entsize: U32::new(endian, section.sh_entsize as u32), }; self.buffer.write(§ion); } } /// Add a section name to the section header string table. /// /// This will be stored in the `.shstrtab` section. /// /// This must be called before [`Self::reserve_shstrtab`]. pub fn add_section_name(&mut self, name: &'a [u8]) -> StringId { debug_assert_eq!(self.shstrtab_offset, 0); self.shstrtab.add(name) } /// Reserve the range for the section header string table. /// /// This range is used for a section named `.shstrtab`. /// /// This function does nothing if no sections were reserved. /// This must be called after [`Self::add_section_name`]. /// and other functions that reserve section names and indices. pub fn reserve_shstrtab(&mut self) { debug_assert_eq!(self.shstrtab_offset, 0); if self.section_num == 0 { return; } // Start with null section name. self.shstrtab_data = vec![0]; self.shstrtab.write(1, &mut self.shstrtab_data); self.shstrtab_offset = self.reserve(self.shstrtab_data.len(), 1); } /// Write the section header string table. /// /// This function does nothing if the section was not reserved. pub fn write_shstrtab(&mut self) { if self.shstrtab_offset == 0 { return; } debug_assert_eq!(self.shstrtab_offset, self.buffer.len()); self.buffer.write_bytes(&self.shstrtab_data); } /// Reserve the section index for the section header string table. /// /// This must be called before [`Self::reserve_shstrtab`] /// and [`Self::reserve_section_headers`]. pub fn reserve_shstrtab_section_index(&mut self) -> SectionIndex { self.reserve_shstrtab_section_index_with_name(&b".shstrtab"[..]) } /// Reserve the section index for the section header string table. /// /// This must be called before [`Self::reserve_shstrtab`] /// and [`Self::reserve_section_headers`]. pub fn reserve_shstrtab_section_index_with_name(&mut self, name: &'a [u8]) -> SectionIndex { debug_assert_eq!(self.shstrtab_index, SectionIndex(0)); self.shstrtab_str_id = Some(self.add_section_name(name)); self.shstrtab_index = self.reserve_section_index(); self.shstrtab_index } /// Write the section header for the section header string table. /// /// This function does nothing if the section index was not reserved. pub fn write_shstrtab_section_header(&mut self) { if self.shstrtab_index == SectionIndex(0) { return; } self.write_section_header(&SectionHeader { name: self.shstrtab_str_id, sh_type: elf::SHT_STRTAB, sh_flags: 0, sh_addr: 0, sh_offset: self.shstrtab_offset as u64, sh_size: self.shstrtab_data.len() as u64, sh_link: 0, sh_info: 0, sh_addralign: 1, sh_entsize: 0, }); } /// Add a string to the string table. /// /// This will be stored in the `.strtab` section. /// /// This must be called before [`Self::reserve_strtab`]. pub fn add_string(&mut self, name: &'a [u8]) -> StringId { debug_assert_eq!(self.strtab_offset, 0); self.need_strtab = true; self.strtab.add(name) } /// Return true if `.strtab` is needed. pub fn strtab_needed(&self) -> bool { self.need_strtab } /// Require the string table even if no strings were added. pub fn require_strtab(&mut self) { self.need_strtab = true; } /// Reserve the range for the string table. /// /// This range is used for a section named `.strtab`. /// /// This function does nothing if a string table is not required. /// This must be called after [`Self::add_string`]. pub fn reserve_strtab(&mut self) { debug_assert_eq!(self.strtab_offset, 0); if !self.need_strtab { return; } // Start with null string. self.strtab_data = vec![0]; self.strtab.write(1, &mut self.strtab_data); self.strtab_offset = self.reserve(self.strtab_data.len(), 1); } /// Write the string table. /// /// This function does nothing if the section was not reserved. pub fn write_strtab(&mut self) { if self.strtab_offset == 0 { return; } debug_assert_eq!(self.strtab_offset, self.buffer.len()); self.buffer.write_bytes(&self.strtab_data); } /// Reserve the section index for the string table. /// /// You should check [`Self::strtab_needed`] before calling this /// unless you have other means of knowing if this section is needed. /// /// This must be called before [`Self::reserve_section_headers`]. pub fn reserve_strtab_section_index(&mut self) -> SectionIndex { self.reserve_strtab_section_index_with_name(&b".strtab"[..]) } /// Reserve the section index for the string table. /// /// You should check [`Self::strtab_needed`] before calling this /// unless you have other means of knowing if this section is needed. /// /// This must be called before [`Self::reserve_section_headers`]. pub fn reserve_strtab_section_index_with_name(&mut self, name: &'a [u8]) -> SectionIndex { debug_assert_eq!(self.strtab_index, SectionIndex(0)); self.strtab_str_id = Some(self.add_section_name(name)); self.strtab_index = self.reserve_section_index(); self.strtab_index } /// Write the section header for the string table. /// /// This function does nothing if the section index was not reserved. pub fn write_strtab_section_header(&mut self) { if self.strtab_index == SectionIndex(0) { return; } self.write_section_header(&SectionHeader { name: self.strtab_str_id, sh_type: elf::SHT_STRTAB, sh_flags: 0, sh_addr: 0, sh_offset: self.strtab_offset as u64, sh_size: self.strtab_data.len() as u64, sh_link: 0, sh_info: 0, sh_addralign: 1, sh_entsize: 0, }); } /// Reserve the null symbol table entry. /// /// This will be stored in the `.symtab` section. /// /// The null symbol table entry is usually automatically reserved, /// but this can be used to force an empty symbol table. /// /// This must be called before [`Self::reserve_symtab`]. pub fn reserve_null_symbol_index(&mut self) -> SymbolIndex { debug_assert_eq!(self.symtab_offset, 0); debug_assert_eq!(self.symtab_num, 0); self.symtab_num = 1; // The symtab must link to a strtab. self.need_strtab = true; SymbolIndex(0) } /// Reserve a symbol table entry. /// /// This will be stored in the `.symtab` section. /// /// `section_index` is used to determine whether `.symtab_shndx` is required. /// /// Automatically also reserves the null symbol if required. /// Callers may assume that the returned indices will be sequential /// starting at 1. /// /// This must be called before [`Self::reserve_symtab`] and /// [`Self::reserve_symtab_shndx`]. pub fn reserve_symbol_index(&mut self, section_index: Option) -> SymbolIndex { debug_assert_eq!(self.symtab_offset, 0); debug_assert_eq!(self.symtab_shndx_offset, 0); if self.symtab_num == 0 { self.symtab_num = 1; // The symtab must link to a strtab. self.need_strtab = true; } let index = self.symtab_num; self.symtab_num += 1; if let Some(section_index) = section_index { if section_index.0 >= elf::SHN_LORESERVE.into() { self.need_symtab_shndx = true; } } SymbolIndex(index) } /// Return the number of reserved symbol table entries. /// /// Includes the null symbol. pub fn symbol_count(&self) -> u32 { self.symtab_num } /// Reserve the range for the symbol table. /// /// This range is used for a section named `.symtab`. /// This function does nothing if no symbols were reserved. /// This must be called after [`Self::reserve_symbol_index`]. pub fn reserve_symtab(&mut self) { debug_assert_eq!(self.symtab_offset, 0); if self.symtab_num == 0 { return; } self.symtab_offset = self.reserve( self.symtab_num as usize * self.class().sym_size(), self.elf_align, ); } /// Write the null symbol. /// /// This must be the first symbol that is written. /// This function does nothing if no symbols were reserved. pub fn write_null_symbol(&mut self) { if self.symtab_num == 0 { return; } util::write_align(self.buffer, self.elf_align); debug_assert_eq!(self.symtab_offset, self.buffer.len()); if self.is_64 { self.buffer.write(&elf::Sym64::::default()); } else { self.buffer.write(&elf::Sym32::::default()); } if self.need_symtab_shndx { self.symtab_shndx_data.write_pod(&U32::new(self.endian, 0)); } } /// Write a symbol. pub fn write_symbol(&mut self, sym: &Sym) { let st_name = if let Some(name) = sym.name { self.strtab.get_offset(name) as u32 } else { 0 }; let st_shndx = if let Some(section) = sym.section { if section.0 >= elf::SHN_LORESERVE as u32 { elf::SHN_XINDEX } else { section.0 as u16 } } else { sym.st_shndx }; let endian = self.endian; if self.is_64 { let sym = elf::Sym64 { st_name: U32::new(endian, st_name), st_info: sym.st_info, st_other: sym.st_other, st_shndx: U16::new(endian, st_shndx), st_value: U64::new(endian, sym.st_value), st_size: U64::new(endian, sym.st_size), }; self.buffer.write(&sym); } else { let sym = elf::Sym32 { st_name: U32::new(endian, st_name), st_info: sym.st_info, st_other: sym.st_other, st_shndx: U16::new(endian, st_shndx), st_value: U32::new(endian, sym.st_value as u32), st_size: U32::new(endian, sym.st_size as u32), }; self.buffer.write(&sym); } if self.need_symtab_shndx { let section_index = sym.section.unwrap_or(SectionIndex(0)); self.symtab_shndx_data .write_pod(&U32::new(self.endian, section_index.0)); } } /// Reserve the section index for the symbol table. /// /// This must be called before [`Self::reserve_section_headers`]. pub fn reserve_symtab_section_index(&mut self) -> SectionIndex { self.reserve_symtab_section_index_with_name(&b".symtab"[..]) } /// Reserve the section index for the symbol table. /// /// This must be called before [`Self::reserve_section_headers`]. pub fn reserve_symtab_section_index_with_name(&mut self, name: &'a [u8]) -> SectionIndex { debug_assert_eq!(self.symtab_index, SectionIndex(0)); self.symtab_str_id = Some(self.add_section_name(name)); self.symtab_index = self.reserve_section_index(); self.symtab_index } /// Return the section index of the symbol table. pub fn symtab_index(&mut self) -> SectionIndex { self.symtab_index } /// Write the section header for the symbol table. /// /// This function does nothing if the section index was not reserved. pub fn write_symtab_section_header(&mut self, num_local: u32) { if self.symtab_index == SectionIndex(0) { return; } self.write_section_header(&SectionHeader { name: self.symtab_str_id, sh_type: elf::SHT_SYMTAB, sh_flags: 0, sh_addr: 0, sh_offset: self.symtab_offset as u64, sh_size: self.symtab_num as u64 * self.class().sym_size() as u64, sh_link: self.strtab_index.0, sh_info: num_local, sh_addralign: self.elf_align as u64, sh_entsize: self.class().sym_size() as u64, }); } /// Return true if `.symtab_shndx` is needed. pub fn symtab_shndx_needed(&self) -> bool { self.need_symtab_shndx } /// Require the extended section indices for the symbol table even /// if no section indices are too large. pub fn require_symtab_shndx(&mut self) { self.need_symtab_shndx = true; } /// Reserve the range for the extended section indices for the symbol table. /// /// This range is used for a section named `.symtab_shndx`. /// This also reserves a section index. /// /// This function does nothing if extended section indices are not needed. /// This must be called after [`Self::reserve_symbol_index`]. pub fn reserve_symtab_shndx(&mut self) { debug_assert_eq!(self.symtab_shndx_offset, 0); if !self.need_symtab_shndx { return; } self.symtab_shndx_offset = self.reserve(self.symtab_num as usize * 4, ALIGN_SYMTAB_SHNDX); self.symtab_shndx_data.reserve(self.symtab_num as usize * 4); } /// Write the extended section indices for the symbol table. /// /// This function does nothing if the section was not reserved. pub fn write_symtab_shndx(&mut self) { if self.symtab_shndx_offset == 0 { return; } util::write_align(self.buffer, ALIGN_SYMTAB_SHNDX); debug_assert_eq!(self.symtab_shndx_offset, self.buffer.len()); debug_assert_eq!(self.symtab_num as usize * 4, self.symtab_shndx_data.len()); self.buffer.write_bytes(&self.symtab_shndx_data); } /// Reserve the section index for the extended section indices symbol table. /// /// You should check [`Self::symtab_shndx_needed`] before calling this /// unless you have other means of knowing if this section is needed. /// /// This must be called before [`Self::reserve_section_headers`]. pub fn reserve_symtab_shndx_section_index(&mut self) -> SectionIndex { self.reserve_symtab_shndx_section_index_with_name(&b".symtab_shndx"[..]) } /// Reserve the section index for the extended section indices symbol table. /// /// You should check [`Self::symtab_shndx_needed`] before calling this /// unless you have other means of knowing if this section is needed. /// /// This must be called before [`Self::reserve_section_headers`]. pub fn reserve_symtab_shndx_section_index_with_name(&mut self, name: &'a [u8]) -> SectionIndex { debug_assert!(self.symtab_shndx_str_id.is_none()); self.symtab_shndx_str_id = Some(self.add_section_name(name)); self.reserve_section_index() } /// Write the section header for the extended section indices for the symbol table. /// /// This function does nothing if the section index was not reserved. pub fn write_symtab_shndx_section_header(&mut self) { if self.symtab_shndx_str_id.is_none() { return; } let sh_size = if self.symtab_shndx_offset == 0 { 0 } else { (self.symtab_num * 4) as u64 }; self.write_section_header(&SectionHeader { name: self.symtab_shndx_str_id, sh_type: elf::SHT_SYMTAB_SHNDX, sh_flags: 0, sh_addr: 0, sh_offset: self.symtab_shndx_offset as u64, sh_size, sh_link: self.symtab_index.0, sh_info: 0, sh_addralign: ALIGN_SYMTAB_SHNDX as u64, sh_entsize: 4, }); } /// Add a string to the dynamic string table. /// /// This will be stored in the `.dynstr` section. /// /// This must be called before [`Self::reserve_dynstr`]. pub fn add_dynamic_string(&mut self, name: &'a [u8]) -> StringId { debug_assert_eq!(self.dynstr_offset, 0); self.need_dynstr = true; self.dynstr.add(name) } /// Get a string that was previously added to the dynamic string table. /// /// Panics if the string was not added. pub fn get_dynamic_string(&self, name: &'a [u8]) -> StringId { self.dynstr.get_id(name) } /// Return true if `.dynstr` is needed. pub fn dynstr_needed(&self) -> bool { self.need_dynstr } /// Require the dynamic string table even if no strings were added. pub fn require_dynstr(&mut self) { self.need_dynstr = true; } /// Reserve the range for the dynamic string table. /// /// This range is used for a section named `.dynstr`. /// /// This function does nothing if no dynamic strings were defined. /// This must be called after [`Self::add_dynamic_string`]. pub fn reserve_dynstr(&mut self) -> usize { debug_assert_eq!(self.dynstr_offset, 0); if !self.need_dynstr { return 0; } // Start with null string. self.dynstr_data = vec![0]; self.dynstr.write(1, &mut self.dynstr_data); self.dynstr_offset = self.reserve(self.dynstr_data.len(), 1); self.dynstr_offset } /// Return the size of the dynamic string table. /// /// This must be called after [`Self::reserve_dynstr`]. pub fn dynstr_len(&mut self) -> usize { debug_assert_ne!(self.dynstr_offset, 0); self.dynstr_data.len() } /// Write the dynamic string table. /// /// This function does nothing if the section was not reserved. pub fn write_dynstr(&mut self) { if self.dynstr_offset == 0 { return; } debug_assert_eq!(self.dynstr_offset, self.buffer.len()); self.buffer.write_bytes(&self.dynstr_data); } /// Reserve the section index for the dynamic string table. /// /// You should check [`Self::dynstr_needed`] before calling this /// unless you have other means of knowing if this section is needed. /// /// This must be called before [`Self::reserve_section_headers`]. pub fn reserve_dynstr_section_index(&mut self) -> SectionIndex { self.reserve_dynstr_section_index_with_name(&b".dynstr"[..]) } /// Reserve the section index for the dynamic string table. /// /// You should check [`Self::dynstr_needed`] before calling this /// unless you have other means of knowing if this section is needed. /// /// This must be called before [`Self::reserve_section_headers`]. pub fn reserve_dynstr_section_index_with_name(&mut self, name: &'a [u8]) -> SectionIndex { debug_assert_eq!(self.dynstr_index, SectionIndex(0)); self.dynstr_str_id = Some(self.add_section_name(name)); self.dynstr_index = self.reserve_section_index(); self.dynstr_index } /// Return the section index of the dynamic string table. pub fn dynstr_index(&mut self) -> SectionIndex { self.dynstr_index } /// Write the section header for the dynamic string table. /// /// This function does nothing if the section index was not reserved. pub fn write_dynstr_section_header(&mut self, sh_addr: u64) { if self.dynstr_index == SectionIndex(0) { return; } self.write_section_header(&SectionHeader { name: self.dynstr_str_id, sh_type: elf::SHT_STRTAB, sh_flags: elf::SHF_ALLOC.into(), sh_addr, sh_offset: self.dynstr_offset as u64, sh_size: self.dynstr_data.len() as u64, sh_link: 0, sh_info: 0, sh_addralign: 1, sh_entsize: 0, }); } /// Reserve the null dynamic symbol table entry. /// /// This will be stored in the `.dynsym` section. /// /// The null dynamic symbol table entry is usually automatically reserved, /// but this can be used to force an empty dynamic symbol table. /// /// This must be called before [`Self::reserve_dynsym`]. pub fn reserve_null_dynamic_symbol_index(&mut self) -> SymbolIndex { debug_assert_eq!(self.dynsym_offset, 0); debug_assert_eq!(self.dynsym_num, 0); self.dynsym_num = 1; SymbolIndex(0) } /// Reserve a dynamic symbol table entry. /// /// This will be stored in the `.dynsym` section. /// /// Automatically also reserves the null symbol if required. /// Callers may assume that the returned indices will be sequential /// starting at 1. /// /// This must be called before [`Self::reserve_dynsym`]. pub fn reserve_dynamic_symbol_index(&mut self) -> SymbolIndex { debug_assert_eq!(self.dynsym_offset, 0); if self.dynsym_num == 0 { self.dynsym_num = 1; } let index = self.dynsym_num; self.dynsym_num += 1; SymbolIndex(index) } /// Return the number of reserved dynamic symbols. /// /// Includes the null symbol. pub fn dynamic_symbol_count(&mut self) -> u32 { self.dynsym_num } /// Reserve the range for the dynamic symbol table. /// /// This range is used for a section named `.dynsym`. /// /// This function does nothing if no dynamic symbols were reserved. /// This must be called after [`Self::reserve_dynamic_symbol_index`]. pub fn reserve_dynsym(&mut self) -> usize { debug_assert_eq!(self.dynsym_offset, 0); if self.dynsym_num == 0 { return 0; } self.dynsym_offset = self.reserve( self.dynsym_num as usize * self.class().sym_size(), self.elf_align, ); self.dynsym_offset } /// Write the null dynamic symbol. /// /// This must be the first dynamic symbol that is written. /// This function does nothing if no dynamic symbols were reserved. pub fn write_null_dynamic_symbol(&mut self) { if self.dynsym_num == 0 { return; } util::write_align(self.buffer, self.elf_align); debug_assert_eq!(self.dynsym_offset, self.buffer.len()); if self.is_64 { self.buffer.write(&elf::Sym64::::default()); } else { self.buffer.write(&elf::Sym32::::default()); } } /// Write a dynamic symbol. pub fn write_dynamic_symbol(&mut self, sym: &Sym) { let st_name = if let Some(name) = sym.name { self.dynstr.get_offset(name) as u32 } else { 0 }; let st_shndx = if let Some(section) = sym.section { if section.0 >= elf::SHN_LORESERVE as u32 { // TODO: we don't actually write out .dynsym_shndx yet. // This is unlikely to be needed though. elf::SHN_XINDEX } else { section.0 as u16 } } else { sym.st_shndx }; let endian = self.endian; if self.is_64 { let sym = elf::Sym64 { st_name: U32::new(endian, st_name), st_info: sym.st_info, st_other: sym.st_other, st_shndx: U16::new(endian, st_shndx), st_value: U64::new(endian, sym.st_value), st_size: U64::new(endian, sym.st_size), }; self.buffer.write(&sym); } else { let sym = elf::Sym32 { st_name: U32::new(endian, st_name), st_info: sym.st_info, st_other: sym.st_other, st_shndx: U16::new(endian, st_shndx), st_value: U32::new(endian, sym.st_value as u32), st_size: U32::new(endian, sym.st_size as u32), }; self.buffer.write(&sym); } } /// Reserve the section index for the dynamic symbol table. /// /// This must be called before [`Self::reserve_section_headers`]. pub fn reserve_dynsym_section_index(&mut self) -> SectionIndex { self.reserve_dynsym_section_index_with_name(&b".dynsym"[..]) } /// Reserve the section index for the dynamic symbol table. /// /// This must be called before [`Self::reserve_section_headers`]. pub fn reserve_dynsym_section_index_with_name(&mut self, name: &'a [u8]) -> SectionIndex { debug_assert_eq!(self.dynsym_index, SectionIndex(0)); self.dynsym_str_id = Some(self.add_section_name(name)); self.dynsym_index = self.reserve_section_index(); self.dynsym_index } /// Return the section index of the dynamic symbol table. pub fn dynsym_index(&mut self) -> SectionIndex { self.dynsym_index } /// Write the section header for the dynamic symbol table. /// /// This function does nothing if the section index was not reserved. pub fn write_dynsym_section_header(&mut self, sh_addr: u64, num_local: u32) { if self.dynsym_index == SectionIndex(0) { return; } self.write_section_header(&SectionHeader { name: self.dynsym_str_id, sh_type: elf::SHT_DYNSYM, sh_flags: elf::SHF_ALLOC.into(), sh_addr, sh_offset: self.dynsym_offset as u64, sh_size: self.dynsym_num as u64 * self.class().sym_size() as u64, sh_link: self.dynstr_index.0, sh_info: num_local, sh_addralign: self.elf_align as u64, sh_entsize: self.class().sym_size() as u64, }); } /// Reserve the range for the `.dynamic` section. /// /// This function does nothing if `dynamic_num` is zero. pub fn reserve_dynamic(&mut self, dynamic_num: usize) -> usize { debug_assert_eq!(self.dynamic_offset, 0); if dynamic_num == 0 { return 0; } self.dynamic_num = dynamic_num; self.dynamic_offset = self.reserve_dynamics(dynamic_num); self.dynamic_offset } /// Write alignment padding bytes prior to the `.dynamic` section. /// /// This function does nothing if the section was not reserved. pub fn write_align_dynamic(&mut self) { if self.dynamic_offset == 0 { return; } util::write_align(self.buffer, self.elf_align); debug_assert_eq!(self.dynamic_offset, self.buffer.len()); } /// Reserve a file range for the given number of dynamic entries. /// /// Returns the offset of the range. pub fn reserve_dynamics(&mut self, dynamic_num: usize) -> usize { self.reserve(dynamic_num * self.class().dyn_size(), self.elf_align) } /// Write a dynamic string entry. pub fn write_dynamic_string(&mut self, tag: u32, id: StringId) { self.write_dynamic(tag, self.dynstr.get_offset(id) as u64); } /// Write a dynamic value entry. pub fn write_dynamic(&mut self, d_tag: u32, d_val: u64) { let endian = self.endian; if self.is_64 { let d = elf::Dyn64 { d_tag: U64::new(endian, d_tag.into()), d_val: U64::new(endian, d_val), }; self.buffer.write(&d); } else { let d = elf::Dyn32 { d_tag: U32::new(endian, d_tag), d_val: U32::new(endian, d_val as u32), }; self.buffer.write(&d); } } /// Reserve the section index for the dynamic table. pub fn reserve_dynamic_section_index(&mut self) -> SectionIndex { debug_assert!(self.dynamic_str_id.is_none()); self.dynamic_str_id = Some(self.add_section_name(&b".dynamic"[..])); self.reserve_section_index() } /// Write the section header for the dynamic table. /// /// This function does nothing if the section index was not reserved. pub fn write_dynamic_section_header(&mut self, sh_addr: u64) { if self.dynamic_str_id.is_none() { return; } self.write_section_header(&SectionHeader { name: self.dynamic_str_id, sh_type: elf::SHT_DYNAMIC, sh_flags: (elf::SHF_WRITE | elf::SHF_ALLOC).into(), sh_addr, sh_offset: self.dynamic_offset as u64, sh_size: (self.dynamic_num * self.class().dyn_size()) as u64, sh_link: self.dynstr_index.0, sh_info: 0, sh_addralign: self.elf_align as u64, sh_entsize: self.class().dyn_size() as u64, }); } /// Reserve a file range for a SysV hash section. /// /// `symbol_count` is the number of symbols in the hash, /// not the total number of symbols. pub fn reserve_hash(&mut self, bucket_count: u32, chain_count: u32) -> usize { self.hash_size = self.class().hash_size(bucket_count, chain_count); self.hash_offset = self.reserve(self.hash_size, ALIGN_HASH); self.hash_offset } /// Write a SysV hash section. /// /// `chain_count` is the number of symbols in the hash. /// The argument to `hash` will be in the range `0..chain_count`. pub fn write_hash(&mut self, bucket_count: u32, chain_count: u32, hash: F) where F: Fn(u32) -> Option, { let mut buckets = vec![U32::new(self.endian, 0); bucket_count as usize]; let mut chains = vec![U32::new(self.endian, 0); chain_count as usize]; for i in 0..chain_count { if let Some(hash) = hash(i) { let bucket = hash % bucket_count; chains[i as usize] = buckets[bucket as usize]; buckets[bucket as usize] = U32::new(self.endian, i); } } util::write_align(self.buffer, ALIGN_HASH); debug_assert_eq!(self.hash_offset, self.buffer.len()); self.buffer.write(&elf::HashHeader { bucket_count: U32::new(self.endian, bucket_count), chain_count: U32::new(self.endian, chain_count), }); self.buffer.write_slice(&buckets); self.buffer.write_slice(&chains); } /// Reserve the section index for the SysV hash table. pub fn reserve_hash_section_index(&mut self) -> SectionIndex { self.reserve_hash_section_index_with_name(&b".hash"[..]) } /// Reserve the section index for the SysV hash table. pub fn reserve_hash_section_index_with_name(&mut self, name: &'a [u8]) -> SectionIndex { debug_assert!(self.hash_str_id.is_none()); self.hash_str_id = Some(self.add_section_name(name)); self.reserve_section_index() } /// Write the section header for the SysV hash table. /// /// This function does nothing if the section index was not reserved. pub fn write_hash_section_header(&mut self, sh_addr: u64) { if self.hash_str_id.is_none() { return; } self.write_section_header(&SectionHeader { name: self.hash_str_id, sh_type: elf::SHT_HASH, sh_flags: elf::SHF_ALLOC.into(), sh_addr, sh_offset: self.hash_offset as u64, sh_size: self.hash_size as u64, sh_link: self.dynsym_index.0, sh_info: 0, sh_addralign: ALIGN_HASH as u64, sh_entsize: 4, }); } /// Reserve a file range for a GNU hash section. /// /// `symbol_count` is the number of symbols in the hash, /// not the total number of symbols. pub fn reserve_gnu_hash( &mut self, bloom_count: u32, bucket_count: u32, symbol_count: u32, ) -> usize { self.gnu_hash_size = self .class() .gnu_hash_size(bloom_count, bucket_count, symbol_count); self.gnu_hash_offset = self.reserve(self.gnu_hash_size, self.elf_align); self.gnu_hash_offset } /// Write a GNU hash section. /// /// `symbol_count` is the number of symbols in the hash. /// The argument to `hash` will be in the range `0..symbol_count`. /// /// This requires that symbols are already sorted by bucket. pub fn write_gnu_hash( &mut self, symbol_base: u32, bloom_shift: u32, bloom_count: u32, bucket_count: u32, symbol_count: u32, hash: F, ) where F: Fn(u32) -> u32, { util::write_align(self.buffer, self.elf_align); debug_assert_eq!(self.gnu_hash_offset, self.buffer.len()); self.buffer.write(&elf::GnuHashHeader { bucket_count: U32::new(self.endian, bucket_count), symbol_base: U32::new(self.endian, symbol_base), bloom_count: U32::new(self.endian, bloom_count), bloom_shift: U32::new(self.endian, bloom_shift), }); // Calculate and write bloom filter. if self.is_64 { let mut bloom_filters = vec![0; bloom_count as usize]; for i in 0..symbol_count { let h = hash(i); bloom_filters[((h / 64) & (bloom_count - 1)) as usize] |= 1 << (h % 64) | 1 << ((h >> bloom_shift) % 64); } for bloom_filter in bloom_filters { self.buffer.write(&U64::new(self.endian, bloom_filter)); } } else { let mut bloom_filters = vec![0; bloom_count as usize]; for i in 0..symbol_count { let h = hash(i); bloom_filters[((h / 32) & (bloom_count - 1)) as usize] |= 1 << (h % 32) | 1 << ((h >> bloom_shift) % 32); } for bloom_filter in bloom_filters { self.buffer.write(&U32::new(self.endian, bloom_filter)); } } // Write buckets. // // This requires that symbols are already sorted by bucket. let mut bucket = 0; for i in 0..symbol_count { let symbol_bucket = hash(i) % bucket_count; while bucket < symbol_bucket { self.buffer.write(&U32::new(self.endian, 0)); bucket += 1; } if bucket == symbol_bucket { self.buffer.write(&U32::new(self.endian, symbol_base + i)); bucket += 1; } } while bucket < bucket_count { self.buffer.write(&U32::new(self.endian, 0)); bucket += 1; } // Write hash values. for i in 0..symbol_count { let mut h = hash(i); if i == symbol_count - 1 || h % bucket_count != hash(i + 1) % bucket_count { h |= 1; } else { h &= !1; } self.buffer.write(&U32::new(self.endian, h)); } } /// Reserve the section index for the GNU hash table. pub fn reserve_gnu_hash_section_index(&mut self) -> SectionIndex { self.reserve_gnu_hash_section_index_with_name(&b".gnu.hash"[..]) } /// Reserve the section index for the GNU hash table. pub fn reserve_gnu_hash_section_index_with_name(&mut self, name: &'a [u8]) -> SectionIndex { debug_assert!(self.gnu_hash_str_id.is_none()); self.gnu_hash_str_id = Some(self.add_section_name(name)); self.reserve_section_index() } /// Write the section header for the GNU hash table. /// /// This function does nothing if the section index was not reserved. pub fn write_gnu_hash_section_header(&mut self, sh_addr: u64) { if self.gnu_hash_str_id.is_none() { return; } self.write_section_header(&SectionHeader { name: self.gnu_hash_str_id, sh_type: elf::SHT_GNU_HASH, sh_flags: elf::SHF_ALLOC.into(), sh_addr, sh_offset: self.gnu_hash_offset as u64, sh_size: self.gnu_hash_size as u64, sh_link: self.dynsym_index.0, sh_info: 0, sh_addralign: self.elf_align as u64, sh_entsize: if self.is_64 { 0 } else { 4 }, }); } /// Reserve the range for the `.gnu.version` section. /// /// This function does nothing if no dynamic symbols were reserved. pub fn reserve_gnu_versym(&mut self) -> usize { debug_assert_eq!(self.gnu_versym_offset, 0); if self.dynsym_num == 0 { return 0; } self.gnu_versym_offset = self.reserve(self.dynsym_num as usize * 2, ALIGN_GNU_VERSYM); self.gnu_versym_offset } /// Write the null symbol version entry. /// /// This must be the first symbol version that is written. /// This function does nothing if no dynamic symbols were reserved. pub fn write_null_gnu_versym(&mut self) { if self.dynsym_num == 0 { return; } util::write_align(self.buffer, ALIGN_GNU_VERSYM); debug_assert_eq!(self.gnu_versym_offset, self.buffer.len()); self.write_gnu_versym(0); } /// Write a symbol version entry. pub fn write_gnu_versym(&mut self, versym: u16) { self.buffer.write(&U16::new(self.endian, versym)); } /// Reserve the section index for the `.gnu.version` section. pub fn reserve_gnu_versym_section_index(&mut self) -> SectionIndex { self.reserve_gnu_versym_section_index_with_name(&b".gnu.version"[..]) } /// Reserve the section index for the `.gnu.version` section. pub fn reserve_gnu_versym_section_index_with_name(&mut self, name: &'a [u8]) -> SectionIndex { debug_assert!(self.gnu_versym_str_id.is_none()); self.gnu_versym_str_id = Some(self.add_section_name(name)); self.reserve_section_index() } /// Write the section header for the `.gnu.version` section. /// /// This function does nothing if the section index was not reserved. pub fn write_gnu_versym_section_header(&mut self, sh_addr: u64) { if self.gnu_versym_str_id.is_none() { return; } self.write_section_header(&SectionHeader { name: self.gnu_versym_str_id, sh_type: elf::SHT_GNU_VERSYM, sh_flags: elf::SHF_ALLOC.into(), sh_addr, sh_offset: self.gnu_versym_offset as u64, sh_size: self.class().gnu_versym_size(self.dynsym_num as usize) as u64, sh_link: self.dynsym_index.0, sh_info: 0, sh_addralign: ALIGN_GNU_VERSYM as u64, sh_entsize: 2, }); } /// Reserve the range for the `.gnu.version_d` section. pub fn reserve_gnu_verdef(&mut self, verdef_count: usize, verdaux_count: usize) -> usize { debug_assert_eq!(self.gnu_verdef_offset, 0); if verdef_count == 0 { return 0; } self.gnu_verdef_size = self.class().gnu_verdef_size(verdef_count, verdaux_count); self.gnu_verdef_offset = self.reserve(self.gnu_verdef_size, ALIGN_GNU_VERDEF); self.gnu_verdef_count = verdef_count as u16; self.gnu_verdef_remaining = self.gnu_verdef_count; self.gnu_verdef_offset } /// Write alignment padding bytes prior to a `.gnu.version_d` section. pub fn write_align_gnu_verdef(&mut self) { if self.gnu_verdef_offset == 0 { return; } util::write_align(self.buffer, ALIGN_GNU_VERDEF); debug_assert_eq!(self.gnu_verdef_offset, self.buffer.len()); } /// Write a version definition entry. pub fn write_gnu_verdef(&mut self, verdef: &Verdef) { debug_assert_ne!(self.gnu_verdef_remaining, 0); self.gnu_verdef_remaining -= 1; let vd_next = if self.gnu_verdef_remaining == 0 { 0 } else { mem::size_of::>() as u32 + verdef.aux_count as u32 * mem::size_of::>() as u32 }; debug_assert_ne!(verdef.aux_count, 0); self.gnu_verdaux_remaining = verdef.aux_count; let vd_aux = mem::size_of::>() as u32; self.buffer.write(&elf::Verdef { vd_version: U16::new(self.endian, verdef.version), vd_flags: U16::new(self.endian, verdef.flags), vd_ndx: U16::new(self.endian, verdef.index), vd_cnt: U16::new(self.endian, verdef.aux_count), vd_hash: U32::new(self.endian, elf::hash(self.dynstr.get_string(verdef.name))), vd_aux: U32::new(self.endian, vd_aux), vd_next: U32::new(self.endian, vd_next), }); self.write_gnu_verdaux(verdef.name); } /// Write a version definition entry that shares the names of the next definition. /// /// This is typically useful when there are only two versions (including the base) /// and they have the same name. pub fn write_gnu_verdef_shared(&mut self, verdef: &Verdef) { debug_assert_ne!(self.gnu_verdef_remaining, 0); self.gnu_verdef_remaining -= 1; debug_assert_ne!(self.gnu_verdef_remaining, 0); let vd_next = mem::size_of::>() as u32; debug_assert_ne!(verdef.aux_count, 0); self.gnu_verdaux_remaining = 0; let vd_aux = 2 * mem::size_of::>() as u32; self.buffer.write(&elf::Verdef { vd_version: U16::new(self.endian, verdef.version), vd_flags: U16::new(self.endian, verdef.flags), vd_ndx: U16::new(self.endian, verdef.index), vd_cnt: U16::new(self.endian, verdef.aux_count), vd_hash: U32::new(self.endian, elf::hash(self.dynstr.get_string(verdef.name))), vd_aux: U32::new(self.endian, vd_aux), vd_next: U32::new(self.endian, vd_next), }); } /// Write a version definition auxiliary entry. pub fn write_gnu_verdaux(&mut self, name: StringId) { debug_assert_ne!(self.gnu_verdaux_remaining, 0); self.gnu_verdaux_remaining -= 1; let vda_next = if self.gnu_verdaux_remaining == 0 { 0 } else { mem::size_of::>() as u32 }; self.buffer.write(&elf::Verdaux { vda_name: U32::new(self.endian, self.dynstr.get_offset(name) as u32), vda_next: U32::new(self.endian, vda_next), }); } /// Reserve the section index for the `.gnu.version_d` section. pub fn reserve_gnu_verdef_section_index(&mut self) -> SectionIndex { self.reserve_gnu_verdef_section_index_with_name(&b".gnu.version_d"[..]) } /// Reserve the section index for the `.gnu.version_d` section. pub fn reserve_gnu_verdef_section_index_with_name(&mut self, name: &'a [u8]) -> SectionIndex { debug_assert!(self.gnu_verdef_str_id.is_none()); self.gnu_verdef_str_id = Some(self.add_section_name(name)); self.reserve_section_index() } /// Write the section header for the `.gnu.version_d` section. /// /// This function does nothing if the section index was not reserved. pub fn write_gnu_verdef_section_header(&mut self, sh_addr: u64) { if self.gnu_verdef_str_id.is_none() { return; } self.write_section_header(&SectionHeader { name: self.gnu_verdef_str_id, sh_type: elf::SHT_GNU_VERDEF, sh_flags: elf::SHF_ALLOC.into(), sh_addr, sh_offset: self.gnu_verdef_offset as u64, sh_size: self.gnu_verdef_size as u64, sh_link: self.dynstr_index.0, sh_info: self.gnu_verdef_count.into(), sh_addralign: ALIGN_GNU_VERDEF as u64, sh_entsize: 0, }); } /// Reserve the range for the `.gnu.version_r` section. pub fn reserve_gnu_verneed(&mut self, verneed_count: usize, vernaux_count: usize) -> usize { debug_assert_eq!(self.gnu_verneed_offset, 0); if verneed_count == 0 { return 0; } self.gnu_verneed_size = self.class().gnu_verneed_size(verneed_count, vernaux_count); self.gnu_verneed_offset = self.reserve(self.gnu_verneed_size, ALIGN_GNU_VERNEED); self.gnu_verneed_count = verneed_count as u16; self.gnu_verneed_remaining = self.gnu_verneed_count; self.gnu_verneed_offset } /// Write alignment padding bytes prior to a `.gnu.version_r` section. pub fn write_align_gnu_verneed(&mut self) { if self.gnu_verneed_offset == 0 { return; } util::write_align(self.buffer, ALIGN_GNU_VERNEED); debug_assert_eq!(self.gnu_verneed_offset, self.buffer.len()); } /// Write a version need entry. pub fn write_gnu_verneed(&mut self, verneed: &Verneed) { debug_assert_ne!(self.gnu_verneed_remaining, 0); self.gnu_verneed_remaining -= 1; let vn_next = if self.gnu_verneed_remaining == 0 { 0 } else { mem::size_of::>() as u32 + verneed.aux_count as u32 * mem::size_of::>() as u32 }; self.gnu_vernaux_remaining = verneed.aux_count; let vn_aux = if verneed.aux_count == 0 { 0 } else { mem::size_of::>() as u32 }; self.buffer.write(&elf::Verneed { vn_version: U16::new(self.endian, verneed.version), vn_cnt: U16::new(self.endian, verneed.aux_count), vn_file: U32::new(self.endian, self.dynstr.get_offset(verneed.file) as u32), vn_aux: U32::new(self.endian, vn_aux), vn_next: U32::new(self.endian, vn_next), }); } /// Write a version need auxiliary entry. pub fn write_gnu_vernaux(&mut self, vernaux: &Vernaux) { debug_assert_ne!(self.gnu_vernaux_remaining, 0); self.gnu_vernaux_remaining -= 1; let vna_next = if self.gnu_vernaux_remaining == 0 { 0 } else { mem::size_of::>() as u32 }; self.buffer.write(&elf::Vernaux { vna_hash: U32::new(self.endian, elf::hash(self.dynstr.get_string(vernaux.name))), vna_flags: U16::new(self.endian, vernaux.flags), vna_other: U16::new(self.endian, vernaux.index), vna_name: U32::new(self.endian, self.dynstr.get_offset(vernaux.name) as u32), vna_next: U32::new(self.endian, vna_next), }); } /// Reserve the section index for the `.gnu.version_r` section. pub fn reserve_gnu_verneed_section_index(&mut self) -> SectionIndex { self.reserve_gnu_verneed_section_index_with_name(&b".gnu.version_r"[..]) } /// Reserve the section index for the `.gnu.version_r` section. pub fn reserve_gnu_verneed_section_index_with_name(&mut self, name: &'a [u8]) -> SectionIndex { debug_assert!(self.gnu_verneed_str_id.is_none()); self.gnu_verneed_str_id = Some(self.add_section_name(name)); self.reserve_section_index() } /// Write the section header for the `.gnu.version_r` section. /// /// This function does nothing if the section index was not reserved. pub fn write_gnu_verneed_section_header(&mut self, sh_addr: u64) { if self.gnu_verneed_str_id.is_none() { return; } self.write_section_header(&SectionHeader { name: self.gnu_verneed_str_id, sh_type: elf::SHT_GNU_VERNEED, sh_flags: elf::SHF_ALLOC.into(), sh_addr, sh_offset: self.gnu_verneed_offset as u64, sh_size: self.gnu_verneed_size as u64, sh_link: self.dynstr_index.0, sh_info: self.gnu_verneed_count.into(), sh_addralign: ALIGN_GNU_VERNEED as u64, sh_entsize: 0, }); } /// Reserve the section index for the `.gnu.attributes` section. pub fn reserve_gnu_attributes_section_index(&mut self) -> SectionIndex { self.reserve_gnu_attributes_section_index_with_name(&b".gnu.attributes"[..]) } /// Reserve the section index for the `.gnu.attributes` section. pub fn reserve_gnu_attributes_section_index_with_name( &mut self, name: &'a [u8], ) -> SectionIndex { debug_assert!(self.gnu_attributes_str_id.is_none()); self.gnu_attributes_str_id = Some(self.add_section_name(name)); self.reserve_section_index() } /// Reserve the range for the `.gnu.attributes` section. pub fn reserve_gnu_attributes(&mut self, gnu_attributes_size: usize) -> usize { debug_assert_eq!(self.gnu_attributes_offset, 0); if gnu_attributes_size == 0 { return 0; } self.gnu_attributes_size = gnu_attributes_size; self.gnu_attributes_offset = self.reserve(self.gnu_attributes_size, self.elf_align); self.gnu_attributes_offset } /// Write the section header for the `.gnu.attributes` section. /// /// This function does nothing if the section index was not reserved. pub fn write_gnu_attributes_section_header(&mut self) { if self.gnu_attributes_str_id.is_none() { return; } self.write_section_header(&SectionHeader { name: self.gnu_attributes_str_id, sh_type: elf::SHT_GNU_ATTRIBUTES, sh_flags: 0, sh_addr: 0, sh_offset: self.gnu_attributes_offset as u64, sh_size: self.gnu_attributes_size as u64, sh_link: self.dynstr_index.0, sh_info: 0, // TODO sh_addralign: self.elf_align as u64, sh_entsize: 0, }); } /// Write the data for the `.gnu.attributes` section. pub fn write_gnu_attributes(&mut self, data: &[u8]) { if self.gnu_attributes_offset == 0 { return; } util::write_align(self.buffer, self.elf_align); debug_assert_eq!(self.gnu_attributes_offset, self.buffer.len()); self.buffer.write_bytes(data); } /// Reserve a file range for the given number of relocations. /// /// Returns the offset of the range. pub fn reserve_relocations(&mut self, count: usize, is_rela: bool) -> usize { self.reserve(count * self.class().rel_size(is_rela), self.elf_align) } /// Write alignment padding bytes prior to a relocation section. pub fn write_align_relocation(&mut self) { util::write_align(self.buffer, self.elf_align); } /// Write a relocation. pub fn write_relocation(&mut self, is_rela: bool, rel: &Rel) { let endian = self.endian; if self.is_64 { if is_rela { let rel = elf::Rela64 { r_offset: U64::new(endian, rel.r_offset), r_info: elf::Rela64::r_info(endian, self.is_mips64el, rel.r_sym, rel.r_type), r_addend: I64::new(endian, rel.r_addend), }; self.buffer.write(&rel); } else { let rel = elf::Rel64 { r_offset: U64::new(endian, rel.r_offset), r_info: elf::Rel64::r_info(endian, rel.r_sym, rel.r_type), }; self.buffer.write(&rel); } } else { if is_rela { let rel = elf::Rela32 { r_offset: U32::new(endian, rel.r_offset as u32), r_info: elf::Rel32::r_info(endian, rel.r_sym, rel.r_type as u8), r_addend: I32::new(endian, rel.r_addend as i32), }; self.buffer.write(&rel); } else { let rel = elf::Rel32 { r_offset: U32::new(endian, rel.r_offset as u32), r_info: elf::Rel32::r_info(endian, rel.r_sym, rel.r_type as u8), }; self.buffer.write(&rel); } } } /// Write the section header for a relocation section. /// /// `section` is the index of the section the relocations apply to, /// or 0 if none. /// /// `symtab` is the index of the symbol table the relocations refer to, /// or 0 if none. /// /// `offset` is the file offset of the relocations. pub fn write_relocation_section_header( &mut self, name: StringId, section: SectionIndex, symtab: SectionIndex, offset: usize, count: usize, is_rela: bool, ) { self.write_section_header(&SectionHeader { name: Some(name), sh_type: if is_rela { elf::SHT_RELA } else { elf::SHT_REL }, sh_flags: elf::SHF_INFO_LINK.into(), sh_addr: 0, sh_offset: offset as u64, sh_size: (count * self.class().rel_size(is_rela)) as u64, sh_link: symtab.0, sh_info: section.0, sh_addralign: self.elf_align as u64, sh_entsize: self.class().rel_size(is_rela) as u64, }); } /// Reserve a file range for a COMDAT section. /// /// `count` is the number of sections in the COMDAT group. /// /// Returns the offset of the range. pub fn reserve_comdat(&mut self, count: usize) -> usize { self.reserve((count + 1) * 4, 4) } /// Write `GRP_COMDAT` at the start of the COMDAT section. pub fn write_comdat_header(&mut self) { util::write_align(self.buffer, 4); self.buffer.write(&U32::new(self.endian, elf::GRP_COMDAT)); } /// Write an entry in a COMDAT section. pub fn write_comdat_entry(&mut self, entry: SectionIndex) { self.buffer.write(&U32::new(self.endian, entry.0)); } /// Write the section header for a COMDAT section. pub fn write_comdat_section_header( &mut self, name: StringId, symtab: SectionIndex, symbol: SymbolIndex, offset: usize, count: usize, ) { self.write_section_header(&SectionHeader { name: Some(name), sh_type: elf::SHT_GROUP, sh_flags: 0, sh_addr: 0, sh_offset: offset as u64, sh_size: ((count + 1) * 4) as u64, sh_link: symtab.0, sh_info: symbol.0, sh_addralign: 4, sh_entsize: 4, }); } /// Return a helper for writing an attributes section. pub fn attributes_writer(&self) -> AttributesWriter { AttributesWriter::new(self.endian) } } /// A helper for writing an attributes section. /// /// Attributes have a variable length encoding, so it is awkward to write them in a /// single pass. Instead, we build the entire attributes section data in memory, using /// placeholders for unknown lengths that are filled in later. #[allow(missing_debug_implementations)] pub struct AttributesWriter { endian: Endianness, data: Vec, subsection_offset: usize, subsubsection_offset: usize, } impl AttributesWriter { /// Create a new `AttributesWriter` for the given endianness. pub fn new(endian: Endianness) -> Self { AttributesWriter { endian, data: vec![0x41], subsection_offset: 0, subsubsection_offset: 0, } } /// Start a new subsection with the given vendor name. pub fn start_subsection(&mut self, vendor: &[u8]) { debug_assert_eq!(self.subsection_offset, 0); debug_assert_eq!(self.subsubsection_offset, 0); self.subsection_offset = self.data.len(); self.data.extend_from_slice(&[0; 4]); self.data.extend_from_slice(vendor); self.data.push(0); } /// End the subsection. /// /// The subsection length is automatically calculated and written. pub fn end_subsection(&mut self) { debug_assert_ne!(self.subsection_offset, 0); debug_assert_eq!(self.subsubsection_offset, 0); let length = self.data.len() - self.subsection_offset; self.data[self.subsection_offset..][..4] .copy_from_slice(pod::bytes_of(&U32::new(self.endian, length as u32))); self.subsection_offset = 0; } /// Start a new sub-subsection with the given tag. pub fn start_subsubsection(&mut self, tag: u8) { debug_assert_ne!(self.subsection_offset, 0); debug_assert_eq!(self.subsubsection_offset, 0); self.subsubsection_offset = self.data.len(); self.data.push(tag); self.data.extend_from_slice(&[0; 4]); } /// Write a section or symbol index to the sub-subsection. /// /// The user must also call this function to write the terminating 0 index. pub fn write_subsubsection_index(&mut self, index: u32) { debug_assert_ne!(self.subsection_offset, 0); debug_assert_ne!(self.subsubsection_offset, 0); util::write_uleb128(&mut self.data, u64::from(index)); } /// Write raw index data to the sub-subsection. /// /// The terminating 0 index is automatically written. pub fn write_subsubsection_indices(&mut self, indices: &[u8]) { debug_assert_ne!(self.subsection_offset, 0); debug_assert_ne!(self.subsubsection_offset, 0); self.data.extend_from_slice(indices); self.data.push(0); } /// Write an attribute tag to the sub-subsection. pub fn write_attribute_tag(&mut self, tag: u64) { debug_assert_ne!(self.subsection_offset, 0); debug_assert_ne!(self.subsubsection_offset, 0); util::write_uleb128(&mut self.data, tag); } /// Write an attribute integer value to the sub-subsection. pub fn write_attribute_integer(&mut self, value: u64) { debug_assert_ne!(self.subsection_offset, 0); debug_assert_ne!(self.subsubsection_offset, 0); util::write_uleb128(&mut self.data, value); } /// Write an attribute string value to the sub-subsection. /// /// The value must not include the null terminator. pub fn write_attribute_string(&mut self, value: &[u8]) { debug_assert_ne!(self.subsection_offset, 0); debug_assert_ne!(self.subsubsection_offset, 0); self.data.extend_from_slice(value); self.data.push(0); } /// Write raw attribute data to the sub-subsection. pub fn write_subsubsection_attributes(&mut self, attributes: &[u8]) { debug_assert_ne!(self.subsection_offset, 0); debug_assert_ne!(self.subsubsection_offset, 0); self.data.extend_from_slice(attributes); } /// End the sub-subsection. /// /// The sub-subsection length is automatically calculated and written. pub fn end_subsubsection(&mut self) { debug_assert_ne!(self.subsection_offset, 0); debug_assert_ne!(self.subsubsection_offset, 0); let length = self.data.len() - self.subsubsection_offset; self.data[self.subsubsection_offset + 1..][..4] .copy_from_slice(pod::bytes_of(&U32::new(self.endian, length as u32))); self.subsubsection_offset = 0; } /// Return the completed section data. pub fn data(self) -> Vec { debug_assert_eq!(self.subsection_offset, 0); debug_assert_eq!(self.subsubsection_offset, 0); self.data } } /// An ELF file class. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub struct Class { /// Whether the file is 64-bit. pub is_64: bool, } impl Class { /// Return the alignment size. pub fn align(self) -> usize { if self.is_64 { 8 } else { 4 } } /// Return the size of the file header. pub fn file_header_size(self) -> usize { if self.is_64 { mem::size_of::>() } else { mem::size_of::>() } } /// Return the size of a program header. pub fn program_header_size(self) -> usize { if self.is_64 { mem::size_of::>() } else { mem::size_of::>() } } /// Return the size of a section header. pub fn section_header_size(self) -> usize { if self.is_64 { mem::size_of::>() } else { mem::size_of::>() } } /// Return the size of a symbol. pub fn sym_size(self) -> usize { if self.is_64 { mem::size_of::>() } else { mem::size_of::>() } } /// Return the size of a relocation entry. pub fn rel_size(self, is_rela: bool) -> usize { if self.is_64 { if is_rela { mem::size_of::>() } else { mem::size_of::>() } } else { if is_rela { mem::size_of::>() } else { mem::size_of::>() } } } /// Return the size of a dynamic entry. pub fn dyn_size(self) -> usize { if self.is_64 { mem::size_of::>() } else { mem::size_of::>() } } /// Return the size of a hash table. pub fn hash_size(self, bucket_count: u32, chain_count: u32) -> usize { mem::size_of::>() + bucket_count as usize * 4 + chain_count as usize * 4 } /// Return the size of a GNU hash table. pub fn gnu_hash_size(self, bloom_count: u32, bucket_count: u32, symbol_count: u32) -> usize { let bloom_size = if self.is_64 { 8 } else { 4 }; mem::size_of::>() + bloom_count as usize * bloom_size + bucket_count as usize * 4 + symbol_count as usize * 4 } /// Return the size of a GNU symbol version section. pub fn gnu_versym_size(self, symbol_count: usize) -> usize { symbol_count * 2 } /// Return the size of a GNU version definition section. pub fn gnu_verdef_size(self, verdef_count: usize, verdaux_count: usize) -> usize { verdef_count * mem::size_of::>() + verdaux_count * mem::size_of::>() } /// Return the size of a GNU version dependency section. pub fn gnu_verneed_size(self, verneed_count: usize, vernaux_count: usize) -> usize { verneed_count * mem::size_of::>() + vernaux_count * mem::size_of::>() } } /// Native endian version of [`elf::FileHeader64`]. #[allow(missing_docs)] #[derive(Debug, Clone)] pub struct FileHeader { pub os_abi: u8, pub abi_version: u8, pub e_type: u16, pub e_machine: u16, pub e_entry: u64, pub e_flags: u32, } /// Native endian version of [`elf::ProgramHeader64`]. #[allow(missing_docs)] #[derive(Debug, Clone)] pub struct ProgramHeader { pub p_type: u32, pub p_flags: u32, pub p_offset: u64, pub p_vaddr: u64, pub p_paddr: u64, pub p_filesz: u64, pub p_memsz: u64, pub p_align: u64, } /// Native endian version of [`elf::SectionHeader64`]. #[allow(missing_docs)] #[derive(Debug, Clone)] pub struct SectionHeader { pub name: Option, pub sh_type: u32, pub sh_flags: u64, pub sh_addr: u64, pub sh_offset: u64, pub sh_size: u64, pub sh_link: u32, pub sh_info: u32, pub sh_addralign: u64, pub sh_entsize: u64, } /// Native endian version of [`elf::Sym64`]. #[allow(missing_docs)] #[derive(Debug, Clone)] pub struct Sym { pub name: Option, pub section: Option, pub st_info: u8, pub st_other: u8, pub st_shndx: u16, pub st_value: u64, pub st_size: u64, } /// Unified native endian version of [`elf::Rel64`] and [`elf::Rela64`]. #[allow(missing_docs)] #[derive(Debug, Clone)] pub struct Rel { pub r_offset: u64, pub r_sym: u32, pub r_type: u32, pub r_addend: i64, } /// Information required for writing [`elf::Verdef`]. #[allow(missing_docs)] #[derive(Debug, Clone)] pub struct Verdef { pub version: u16, pub flags: u16, pub index: u16, pub aux_count: u16, /// The name for the first [`elf::Verdaux`] entry. pub name: StringId, } /// Information required for writing [`elf::Verneed`]. #[allow(missing_docs)] #[derive(Debug, Clone)] pub struct Verneed { pub version: u16, pub aux_count: u16, pub file: StringId, } /// Information required for writing [`elf::Vernaux`]. #[allow(missing_docs)] #[derive(Debug, Clone)] pub struct Vernaux { pub flags: u16, pub index: u16, pub name: StringId, } object-0.36.5/src/write/macho.rs000064400000000000000000001202361046102023000145470ustar 00000000000000use core::mem; use crate::endian::*; use crate::macho; use crate::write::string::*; use crate::write::util::*; use crate::write::*; #[derive(Default, Clone, Copy)] struct SectionOffsets { index: usize, offset: usize, address: u64, reloc_offset: usize, reloc_count: usize, } #[derive(Default, Clone, Copy)] struct SymbolOffsets { index: usize, str_id: Option, } /// The customizable portion of a [`macho::BuildVersionCommand`]. #[derive(Debug, Default, Clone, Copy)] #[non_exhaustive] // May want to add the tool list? pub struct MachOBuildVersion { /// One of the `PLATFORM_` constants (for example, /// [`object::macho::PLATFORM_MACOS`](macho::PLATFORM_MACOS)). pub platform: u32, /// The minimum OS version, where `X.Y.Z` is encoded in nibbles as /// `xxxx.yy.zz`. pub minos: u32, /// The SDK version as `X.Y.Z`, where `X.Y.Z` is encoded in nibbles as /// `xxxx.yy.zz`. pub sdk: u32, } impl MachOBuildVersion { fn cmdsize(&self) -> u32 { // Same size for both endianness, and we don't have `ntools`. let sz = mem::size_of::>(); debug_assert!(sz <= u32::MAX as usize); sz as u32 } } // Public methods. impl<'a> Object<'a> { /// Specify the Mach-O CPU subtype. /// /// Requires `feature = "macho"`. #[inline] pub fn set_macho_cpu_subtype(&mut self, cpu_subtype: u32) { self.macho_cpu_subtype = Some(cpu_subtype); } /// Specify information for a Mach-O `LC_BUILD_VERSION` command. /// /// Requires `feature = "macho"`. #[inline] pub fn set_macho_build_version(&mut self, info: MachOBuildVersion) { self.macho_build_version = Some(info); } } // Private methods. impl<'a> Object<'a> { pub(crate) fn macho_segment_name(&self, segment: StandardSegment) -> &'static [u8] { match segment { StandardSegment::Text => &b"__TEXT"[..], StandardSegment::Data => &b"__DATA"[..], StandardSegment::Debug => &b"__DWARF"[..], } } pub(crate) fn macho_section_info( &self, section: StandardSection, ) -> (&'static [u8], &'static [u8], SectionKind, SectionFlags) { match section { StandardSection::Text => ( &b"__TEXT"[..], &b"__text"[..], SectionKind::Text, SectionFlags::None, ), StandardSection::Data => ( &b"__DATA"[..], &b"__data"[..], SectionKind::Data, SectionFlags::None, ), StandardSection::ReadOnlyData => ( &b"__TEXT"[..], &b"__const"[..], SectionKind::ReadOnlyData, SectionFlags::None, ), StandardSection::ReadOnlyDataWithRel => ( &b"__DATA"[..], &b"__const"[..], SectionKind::ReadOnlyDataWithRel, SectionFlags::None, ), StandardSection::ReadOnlyString => ( &b"__TEXT"[..], &b"__cstring"[..], SectionKind::ReadOnlyString, SectionFlags::None, ), StandardSection::UninitializedData => ( &b"__DATA"[..], &b"__bss"[..], SectionKind::UninitializedData, SectionFlags::None, ), StandardSection::Tls => ( &b"__DATA"[..], &b"__thread_data"[..], SectionKind::Tls, SectionFlags::None, ), StandardSection::UninitializedTls => ( &b"__DATA"[..], &b"__thread_bss"[..], SectionKind::UninitializedTls, SectionFlags::None, ), StandardSection::TlsVariables => ( &b"__DATA"[..], &b"__thread_vars"[..], SectionKind::TlsVariables, SectionFlags::None, ), StandardSection::Common => ( &b"__DATA"[..], &b"__common"[..], SectionKind::Common, SectionFlags::None, ), StandardSection::GnuProperty => { // Unsupported section. (&[], &[], SectionKind::Note, SectionFlags::None) } } } fn macho_tlv_bootstrap(&mut self) -> SymbolId { match self.tlv_bootstrap { Some(id) => id, None => { let id = self.add_symbol(Symbol { name: b"_tlv_bootstrap".to_vec(), value: 0, size: 0, kind: SymbolKind::Text, scope: SymbolScope::Dynamic, weak: false, section: SymbolSection::Undefined, flags: SymbolFlags::None, }); self.tlv_bootstrap = Some(id); id } } } /// Create the `__thread_vars` entry for a TLS variable. /// /// The symbol given by `symbol_id` will be updated to point to this entry. /// /// A new `SymbolId` will be returned. The caller must update this symbol /// to point to the initializer. /// /// If `symbol_id` is not for a TLS variable, then it is returned unchanged. pub(crate) fn macho_add_thread_var(&mut self, symbol_id: SymbolId) -> SymbolId { let symbol = self.symbol_mut(symbol_id); if symbol.kind != SymbolKind::Tls { return symbol_id; } // Create the initializer symbol. let mut name = symbol.name.clone(); name.extend_from_slice(b"$tlv$init"); let init_symbol_id = self.add_raw_symbol(Symbol { name, value: 0, size: 0, kind: SymbolKind::Tls, scope: SymbolScope::Compilation, weak: false, section: SymbolSection::Undefined, flags: SymbolFlags::None, }); // Add the tlv entry. // Three pointers in size: // - __tlv_bootstrap - used to make sure support exists // - spare pointer - used when mapped by the runtime // - pointer to symbol initializer let section = self.section_id(StandardSection::TlsVariables); let address_size = self.architecture.address_size().unwrap().bytes(); let size = u64::from(address_size) * 3; let data = vec![0; size as usize]; let offset = self.append_section_data(section, &data, u64::from(address_size)); let tlv_bootstrap = self.macho_tlv_bootstrap(); self.add_relocation( section, Relocation { offset, symbol: tlv_bootstrap, addend: 0, flags: RelocationFlags::Generic { kind: RelocationKind::Absolute, encoding: RelocationEncoding::Generic, size: address_size * 8, }, }, ) .unwrap(); self.add_relocation( section, Relocation { offset: offset + u64::from(address_size) * 2, symbol: init_symbol_id, addend: 0, flags: RelocationFlags::Generic { kind: RelocationKind::Absolute, encoding: RelocationEncoding::Generic, size: address_size * 8, }, }, ) .unwrap(); // Update the symbol to point to the tlv. let symbol = self.symbol_mut(symbol_id); symbol.value = offset; symbol.size = size; symbol.section = SymbolSection::Section(section); init_symbol_id } pub(crate) fn macho_translate_relocation(&mut self, reloc: &mut Relocation) -> Result<()> { use RelocationEncoding as E; use RelocationKind as K; let (kind, encoding, mut size) = if let RelocationFlags::Generic { kind, encoding, size, } = reloc.flags { (kind, encoding, size) } else { return Ok(()); }; // Aarch64 relocs of these sizes act as if they are double-word length if self.architecture == Architecture::Aarch64 && matches!(size, 12 | 21 | 26) { size = 32; } let r_length = match size { 8 => 0, 16 => 1, 32 => 2, 64 => 3, _ => return Err(Error(format!("unimplemented reloc size {:?}", reloc))), }; let unsupported_reloc = || Err(Error(format!("unimplemented relocation {:?}", reloc))); let (r_pcrel, r_type) = match self.architecture { Architecture::I386 => match kind { K::Absolute => (false, macho::GENERIC_RELOC_VANILLA), _ => return unsupported_reloc(), }, Architecture::X86_64 => match (kind, encoding) { (K::Absolute, E::Generic) => (false, macho::X86_64_RELOC_UNSIGNED), (K::Relative, E::Generic) => (true, macho::X86_64_RELOC_SIGNED), (K::Relative, E::X86RipRelative) => (true, macho::X86_64_RELOC_SIGNED), (K::Relative, E::X86Branch) => (true, macho::X86_64_RELOC_BRANCH), (K::PltRelative, E::X86Branch) => (true, macho::X86_64_RELOC_BRANCH), (K::GotRelative, E::Generic) => (true, macho::X86_64_RELOC_GOT), (K::GotRelative, E::X86RipRelativeMovq) => (true, macho::X86_64_RELOC_GOT_LOAD), _ => return unsupported_reloc(), }, Architecture::Aarch64 | Architecture::Aarch64_Ilp32 => match (kind, encoding) { (K::Absolute, E::Generic) => (false, macho::ARM64_RELOC_UNSIGNED), (K::Relative, E::AArch64Call) => (true, macho::ARM64_RELOC_BRANCH26), _ => return unsupported_reloc(), }, _ => { return Err(Error(format!( "unimplemented architecture {:?}", self.architecture ))); } }; reloc.flags = RelocationFlags::MachO { r_type, r_pcrel, r_length, }; Ok(()) } pub(crate) fn macho_adjust_addend(&mut self, relocation: &mut Relocation) -> Result { let (r_type, r_pcrel) = if let RelocationFlags::MachO { r_type, r_pcrel, .. } = relocation.flags { (r_type, r_pcrel) } else { return Err(Error(format!("invalid relocation flags {:?}", relocation))); }; if r_pcrel { // For PC relative relocations on some architectures, the // addend does not include the offset required due to the // PC being different from the place of the relocation. // This differs from other file formats, so adjust the // addend here to account for this. let pcrel_offset = match self.architecture { Architecture::I386 => 4, Architecture::X86_64 => match r_type { macho::X86_64_RELOC_SIGNED_1 => 5, macho::X86_64_RELOC_SIGNED_2 => 6, macho::X86_64_RELOC_SIGNED_4 => 8, _ => 4, }, // TODO: maybe missing support for some architectures and relocations _ => 0, }; relocation.addend += pcrel_offset; } // Determine if addend is implicit. let implicit = if self.architecture == Architecture::Aarch64 { match r_type { macho::ARM64_RELOC_BRANCH26 | macho::ARM64_RELOC_PAGE21 | macho::ARM64_RELOC_PAGEOFF12 => false, _ => true, } } else { true }; Ok(implicit) } pub(crate) fn macho_relocation_size(&self, reloc: &Relocation) -> Result { if let RelocationFlags::MachO { r_length, .. } = reloc.flags { Ok(8 << r_length) } else { Err(Error("invalid relocation flags".into())) } } pub(crate) fn macho_write(&self, buffer: &mut dyn WritableBuffer) -> Result<()> { let address_size = self.architecture.address_size().unwrap(); let endian = self.endian; let macho32 = MachO32 { endian }; let macho64 = MachO64 { endian }; let macho: &dyn MachO = match address_size { AddressSize::U8 | AddressSize::U16 | AddressSize::U32 => &macho32, AddressSize::U64 => &macho64, }; let pointer_align = address_size.bytes() as usize; // Calculate offsets of everything, and build strtab. let mut offset = 0; // Calculate size of Mach-O header. offset += macho.mach_header_size(); // Calculate size of commands. let mut ncmds = 0; let command_offset = offset; // Calculate size of segment command and section headers. let segment_command_offset = offset; let segment_command_len = macho.segment_command_size() + self.sections.len() * macho.section_header_size(); offset += segment_command_len; ncmds += 1; // Calculate size of build version. let build_version_offset = offset; if let Some(version) = &self.macho_build_version { offset += version.cmdsize() as usize; ncmds += 1; } // Calculate size of symtab command. let symtab_command_offset = offset; let symtab_command_len = mem::size_of::>(); offset += symtab_command_len; ncmds += 1; // Calculate size of dysymtab command. let dysymtab_command_offset = offset; let dysymtab_command_len = mem::size_of::>(); offset += dysymtab_command_len; ncmds += 1; let sizeofcmds = offset - command_offset; // Calculate size of section data. // Section data can immediately follow the load commands without any alignment padding. let segment_file_offset = offset; let mut section_offsets = vec![SectionOffsets::default(); self.sections.len()]; let mut address = 0; for (index, section) in self.sections.iter().enumerate() { section_offsets[index].index = 1 + index; if !section.is_bss() { address = align_u64(address, section.align); section_offsets[index].address = address; section_offsets[index].offset = segment_file_offset + address as usize; address += section.size; } } let segment_file_size = address as usize; offset += address as usize; for (index, section) in self.sections.iter().enumerate() { if section.is_bss() { debug_assert!(section.data.is_empty()); address = align_u64(address, section.align); section_offsets[index].address = address; address += section.size; } } // Partition symbols and add symbol strings to strtab. let mut strtab = StringTable::default(); let mut symbol_offsets = vec![SymbolOffsets::default(); self.symbols.len()]; let mut local_symbols = vec![]; let mut external_symbols = vec![]; let mut undefined_symbols = vec![]; for (index, symbol) in self.symbols.iter().enumerate() { // The unified API allows creating symbols that we don't emit, so filter // them out here. // // Since we don't actually emit the symbol kind, we validate it here too. match symbol.kind { SymbolKind::Text | SymbolKind::Data | SymbolKind::Tls | SymbolKind::Unknown => {} SymbolKind::File | SymbolKind::Section => continue, SymbolKind::Label => { return Err(Error(format!( "unimplemented symbol `{}` kind {:?}", symbol.name().unwrap_or(""), symbol.kind ))); } } if !symbol.name.is_empty() { symbol_offsets[index].str_id = Some(strtab.add(&symbol.name)); } if symbol.is_undefined() { undefined_symbols.push(index); } else if symbol.is_local() { local_symbols.push(index); } else { external_symbols.push(index); } } external_symbols.sort_by_key(|index| &*self.symbols[*index].name); undefined_symbols.sort_by_key(|index| &*self.symbols[*index].name); // Count symbols. let mut nsyms = 0; for index in local_symbols .iter() .copied() .chain(external_symbols.iter().copied()) .chain(undefined_symbols.iter().copied()) { symbol_offsets[index].index = nsyms; nsyms += 1; } // Calculate size of relocations. for (index, section) in self.sections.iter().enumerate() { let count: usize = section .relocations .iter() .map(|reloc| 1 + usize::from(reloc.addend != 0)) .sum(); if count != 0 { offset = align(offset, pointer_align); section_offsets[index].reloc_offset = offset; section_offsets[index].reloc_count = count; let len = count * mem::size_of::>(); offset += len; } } // Calculate size of symtab. offset = align(offset, pointer_align); let symtab_offset = offset; let symtab_len = nsyms * macho.nlist_size(); offset += symtab_len; // Calculate size of strtab. let strtab_offset = offset; // Start with null name. let mut strtab_data = vec![0]; strtab.write(1, &mut strtab_data); write_align(&mut strtab_data, pointer_align); offset += strtab_data.len(); // Start writing. buffer .reserve(offset) .map_err(|_| Error(String::from("Cannot allocate buffer")))?; // Write file header. let (cputype, mut cpusubtype) = match (self.architecture, self.sub_architecture) { (Architecture::Arm, None) => (macho::CPU_TYPE_ARM, macho::CPU_SUBTYPE_ARM_ALL), (Architecture::Aarch64, None) => (macho::CPU_TYPE_ARM64, macho::CPU_SUBTYPE_ARM64_ALL), (Architecture::Aarch64, Some(SubArchitecture::Arm64E)) => { (macho::CPU_TYPE_ARM64, macho::CPU_SUBTYPE_ARM64E) } (Architecture::Aarch64_Ilp32, None) => { (macho::CPU_TYPE_ARM64_32, macho::CPU_SUBTYPE_ARM64_32_V8) } (Architecture::I386, None) => (macho::CPU_TYPE_X86, macho::CPU_SUBTYPE_I386_ALL), (Architecture::X86_64, None) => (macho::CPU_TYPE_X86_64, macho::CPU_SUBTYPE_X86_64_ALL), (Architecture::PowerPc, None) => { (macho::CPU_TYPE_POWERPC, macho::CPU_SUBTYPE_POWERPC_ALL) } (Architecture::PowerPc64, None) => { (macho::CPU_TYPE_POWERPC64, macho::CPU_SUBTYPE_POWERPC_ALL) } _ => { return Err(Error(format!( "unimplemented architecture {:?} with sub-architecture {:?}", self.architecture, self.sub_architecture ))); } }; if let Some(cpu_subtype) = self.macho_cpu_subtype { cpusubtype = cpu_subtype; } let mut flags = match self.flags { FileFlags::MachO { flags } => flags, _ => 0, }; if self.macho_subsections_via_symbols { flags |= macho::MH_SUBSECTIONS_VIA_SYMBOLS; } macho.write_mach_header( buffer, MachHeader { cputype, cpusubtype, filetype: macho::MH_OBJECT, ncmds, sizeofcmds: sizeofcmds as u32, flags, }, ); // Write segment command. debug_assert_eq!(segment_command_offset, buffer.len()); macho.write_segment_command( buffer, SegmentCommand { cmdsize: segment_command_len as u32, segname: [0; 16], vmaddr: 0, vmsize: address, fileoff: segment_file_offset as u64, filesize: segment_file_size as u64, maxprot: macho::VM_PROT_READ | macho::VM_PROT_WRITE | macho::VM_PROT_EXECUTE, initprot: macho::VM_PROT_READ | macho::VM_PROT_WRITE | macho::VM_PROT_EXECUTE, nsects: self.sections.len() as u32, flags: 0, }, ); // Write section headers. for (index, section) in self.sections.iter().enumerate() { let mut sectname = [0; 16]; sectname .get_mut(..section.name.len()) .ok_or_else(|| { Error(format!( "section name `{}` is too long", section.name().unwrap_or(""), )) })? .copy_from_slice(§ion.name); let mut segname = [0; 16]; segname .get_mut(..section.segment.len()) .ok_or_else(|| { Error(format!( "segment name `{}` is too long", section.segment().unwrap_or(""), )) })? .copy_from_slice(§ion.segment); let flags = if let SectionFlags::MachO { flags } = section.flags { flags } else { match section.kind { SectionKind::Text => { macho::S_ATTR_PURE_INSTRUCTIONS | macho::S_ATTR_SOME_INSTRUCTIONS } SectionKind::Data => 0, SectionKind::ReadOnlyData | SectionKind::ReadOnlyDataWithRel => 0, SectionKind::ReadOnlyString => macho::S_CSTRING_LITERALS, SectionKind::UninitializedData | SectionKind::Common => macho::S_ZEROFILL, SectionKind::Tls => macho::S_THREAD_LOCAL_REGULAR, SectionKind::UninitializedTls => macho::S_THREAD_LOCAL_ZEROFILL, SectionKind::TlsVariables => macho::S_THREAD_LOCAL_VARIABLES, SectionKind::Debug | SectionKind::DebugString => macho::S_ATTR_DEBUG, SectionKind::OtherString => macho::S_CSTRING_LITERALS, SectionKind::Other | SectionKind::Linker | SectionKind::Metadata => 0, SectionKind::Note | SectionKind::Unknown | SectionKind::Elf(_) => { return Err(Error(format!( "unimplemented section `{}` kind {:?}", section.name().unwrap_or(""), section.kind ))); } } }; macho.write_section( buffer, SectionHeader { sectname, segname, addr: section_offsets[index].address, size: section.size, offset: section_offsets[index].offset as u32, align: section.align.trailing_zeros(), reloff: section_offsets[index].reloc_offset as u32, nreloc: section_offsets[index].reloc_count as u32, flags, }, ); } // Write build version. if let Some(version) = &self.macho_build_version { debug_assert_eq!(build_version_offset, buffer.len()); buffer.write(&macho::BuildVersionCommand { cmd: U32::new(endian, macho::LC_BUILD_VERSION), cmdsize: U32::new(endian, version.cmdsize()), platform: U32::new(endian, version.platform), minos: U32::new(endian, version.minos), sdk: U32::new(endian, version.sdk), ntools: U32::new(endian, 0), }); } // Write symtab command. debug_assert_eq!(symtab_command_offset, buffer.len()); let symtab_command = macho::SymtabCommand { cmd: U32::new(endian, macho::LC_SYMTAB), cmdsize: U32::new(endian, symtab_command_len as u32), symoff: U32::new(endian, symtab_offset as u32), nsyms: U32::new(endian, nsyms as u32), stroff: U32::new(endian, strtab_offset as u32), strsize: U32::new(endian, strtab_data.len() as u32), }; buffer.write(&symtab_command); // Write dysymtab command. debug_assert_eq!(dysymtab_command_offset, buffer.len()); let dysymtab_command = macho::DysymtabCommand { cmd: U32::new(endian, macho::LC_DYSYMTAB), cmdsize: U32::new(endian, dysymtab_command_len as u32), ilocalsym: U32::new(endian, 0), nlocalsym: U32::new(endian, local_symbols.len() as u32), iextdefsym: U32::new(endian, local_symbols.len() as u32), nextdefsym: U32::new(endian, external_symbols.len() as u32), iundefsym: U32::new( endian, local_symbols.len() as u32 + external_symbols.len() as u32, ), nundefsym: U32::new(endian, undefined_symbols.len() as u32), tocoff: U32::default(), ntoc: U32::default(), modtaboff: U32::default(), nmodtab: U32::default(), extrefsymoff: U32::default(), nextrefsyms: U32::default(), indirectsymoff: U32::default(), nindirectsyms: U32::default(), extreloff: U32::default(), nextrel: U32::default(), locreloff: U32::default(), nlocrel: U32::default(), }; buffer.write(&dysymtab_command); // Write section data. for (index, section) in self.sections.iter().enumerate() { if !section.is_bss() { buffer.resize(section_offsets[index].offset); buffer.write_bytes(§ion.data); } } debug_assert_eq!(segment_file_offset + segment_file_size, buffer.len()); // Write relocations. for (index, section) in self.sections.iter().enumerate() { if !section.relocations.is_empty() { write_align(buffer, pointer_align); debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len()); let mut write_reloc = |reloc: &Relocation| { let (r_type, r_pcrel, r_length) = if let RelocationFlags::MachO { r_type, r_pcrel, r_length, } = reloc.flags { (r_type, r_pcrel, r_length) } else { return Err(Error("invalid relocation flags".into())); }; // Write explicit addend. if reloc.addend != 0 { let r_type = match self.architecture { Architecture::Aarch64 | Architecture::Aarch64_Ilp32 => { macho::ARM64_RELOC_ADDEND } _ => { return Err(Error(format!("unimplemented relocation {:?}", reloc))) } }; let reloc_info = macho::RelocationInfo { r_address: reloc.offset as u32, r_symbolnum: reloc.addend as u32, r_pcrel: false, r_length, r_extern: false, r_type, }; buffer.write(&reloc_info.relocation(endian)); } let r_extern; let r_symbolnum; let symbol = &self.symbols[reloc.symbol.0]; if symbol.kind == SymbolKind::Section { r_symbolnum = section_offsets[symbol.section.id().unwrap().0].index as u32; r_extern = false; } else { r_symbolnum = symbol_offsets[reloc.symbol.0].index as u32; r_extern = true; } let reloc_info = macho::RelocationInfo { r_address: reloc.offset as u32, r_symbolnum, r_pcrel, r_length, r_extern, r_type, }; buffer.write(&reloc_info.relocation(endian)); Ok(()) }; // Relocations are emitted in descending order as otherwise Apple's // new linker crashes. This matches LLVM's behavior too: // https://github.com/llvm/llvm-project/blob/e9b8cd0c8/llvm/lib/MC/MachObjectWriter.cpp#L1001-L1002 let need_reverse = |relocs: &[Relocation]| { let Some(first) = relocs.first() else { return false; }; let Some(last) = relocs.last() else { return false; }; first.offset < last.offset }; if need_reverse(§ion.relocations) { for reloc in section.relocations.iter().rev() { write_reloc(reloc)?; } } else { for reloc in §ion.relocations { write_reloc(reloc)?; } } } } // Write symtab. write_align(buffer, pointer_align); debug_assert_eq!(symtab_offset, buffer.len()); for index in local_symbols .iter() .copied() .chain(external_symbols.iter().copied()) .chain(undefined_symbols.iter().copied()) { let symbol = &self.symbols[index]; // TODO: N_STAB let (mut n_type, n_sect) = match symbol.section { SymbolSection::Undefined => (macho::N_UNDF | macho::N_EXT, 0), SymbolSection::Absolute => (macho::N_ABS, 0), SymbolSection::Section(id) => (macho::N_SECT, id.0 + 1), SymbolSection::None | SymbolSection::Common => { return Err(Error(format!( "unimplemented symbol `{}` section {:?}", symbol.name().unwrap_or(""), symbol.section ))); } }; match symbol.scope { SymbolScope::Unknown | SymbolScope::Compilation => {} SymbolScope::Linkage => { n_type |= macho::N_EXT | macho::N_PEXT; } SymbolScope::Dynamic => { n_type |= macho::N_EXT; } } let n_desc = if let SymbolFlags::MachO { n_desc } = symbol.flags { n_desc } else { let mut n_desc = 0; if symbol.weak { if symbol.is_undefined() { n_desc |= macho::N_WEAK_REF; } else { n_desc |= macho::N_WEAK_DEF; } } n_desc }; let n_value = match symbol.section.id() { Some(section) => section_offsets[section.0].address + symbol.value, None => symbol.value, }; let n_strx = symbol_offsets[index] .str_id .map(|id| strtab.get_offset(id)) .unwrap_or(0); macho.write_nlist( buffer, Nlist { n_strx: n_strx as u32, n_type, n_sect: n_sect as u8, n_desc, n_value, }, ); } // Write strtab. debug_assert_eq!(strtab_offset, buffer.len()); buffer.write_bytes(&strtab_data); debug_assert_eq!(offset, buffer.len()); Ok(()) } } struct MachHeader { cputype: u32, cpusubtype: u32, filetype: u32, ncmds: u32, sizeofcmds: u32, flags: u32, } struct SegmentCommand { cmdsize: u32, segname: [u8; 16], vmaddr: u64, vmsize: u64, fileoff: u64, filesize: u64, maxprot: u32, initprot: u32, nsects: u32, flags: u32, } pub struct SectionHeader { sectname: [u8; 16], segname: [u8; 16], addr: u64, size: u64, offset: u32, align: u32, reloff: u32, nreloc: u32, flags: u32, } struct Nlist { n_strx: u32, n_type: u8, n_sect: u8, n_desc: u16, n_value: u64, } trait MachO { fn mach_header_size(&self) -> usize; fn segment_command_size(&self) -> usize; fn section_header_size(&self) -> usize; fn nlist_size(&self) -> usize; fn write_mach_header(&self, buffer: &mut dyn WritableBuffer, section: MachHeader); fn write_segment_command(&self, buffer: &mut dyn WritableBuffer, segment: SegmentCommand); fn write_section(&self, buffer: &mut dyn WritableBuffer, section: SectionHeader); fn write_nlist(&self, buffer: &mut dyn WritableBuffer, nlist: Nlist); } struct MachO32 { endian: E, } impl MachO for MachO32 { fn mach_header_size(&self) -> usize { mem::size_of::>() } fn segment_command_size(&self) -> usize { mem::size_of::>() } fn section_header_size(&self) -> usize { mem::size_of::>() } fn nlist_size(&self) -> usize { mem::size_of::>() } fn write_mach_header(&self, buffer: &mut dyn WritableBuffer, header: MachHeader) { let endian = self.endian; let magic = if endian.is_big_endian() { macho::MH_MAGIC } else { macho::MH_CIGAM }; let header = macho::MachHeader32 { magic: U32::new(BigEndian, magic), cputype: U32::new(endian, header.cputype), cpusubtype: U32::new(endian, header.cpusubtype), filetype: U32::new(endian, header.filetype), ncmds: U32::new(endian, header.ncmds), sizeofcmds: U32::new(endian, header.sizeofcmds), flags: U32::new(endian, header.flags), }; buffer.write(&header); } fn write_segment_command(&self, buffer: &mut dyn WritableBuffer, segment: SegmentCommand) { let endian = self.endian; let segment = macho::SegmentCommand32 { cmd: U32::new(endian, macho::LC_SEGMENT), cmdsize: U32::new(endian, segment.cmdsize), segname: segment.segname, vmaddr: U32::new(endian, segment.vmaddr as u32), vmsize: U32::new(endian, segment.vmsize as u32), fileoff: U32::new(endian, segment.fileoff as u32), filesize: U32::new(endian, segment.filesize as u32), maxprot: U32::new(endian, segment.maxprot), initprot: U32::new(endian, segment.initprot), nsects: U32::new(endian, segment.nsects), flags: U32::new(endian, segment.flags), }; buffer.write(&segment); } fn write_section(&self, buffer: &mut dyn WritableBuffer, section: SectionHeader) { let endian = self.endian; let section = macho::Section32 { sectname: section.sectname, segname: section.segname, addr: U32::new(endian, section.addr as u32), size: U32::new(endian, section.size as u32), offset: U32::new(endian, section.offset), align: U32::new(endian, section.align), reloff: U32::new(endian, section.reloff), nreloc: U32::new(endian, section.nreloc), flags: U32::new(endian, section.flags), reserved1: U32::default(), reserved2: U32::default(), }; buffer.write(§ion); } fn write_nlist(&self, buffer: &mut dyn WritableBuffer, nlist: Nlist) { let endian = self.endian; let nlist = macho::Nlist32 { n_strx: U32::new(endian, nlist.n_strx), n_type: nlist.n_type, n_sect: nlist.n_sect, n_desc: U16::new(endian, nlist.n_desc), n_value: U32::new(endian, nlist.n_value as u32), }; buffer.write(&nlist); } } struct MachO64 { endian: E, } impl MachO for MachO64 { fn mach_header_size(&self) -> usize { mem::size_of::>() } fn segment_command_size(&self) -> usize { mem::size_of::>() } fn section_header_size(&self) -> usize { mem::size_of::>() } fn nlist_size(&self) -> usize { mem::size_of::>() } fn write_mach_header(&self, buffer: &mut dyn WritableBuffer, header: MachHeader) { let endian = self.endian; let magic = if endian.is_big_endian() { macho::MH_MAGIC_64 } else { macho::MH_CIGAM_64 }; let header = macho::MachHeader64 { magic: U32::new(BigEndian, magic), cputype: U32::new(endian, header.cputype), cpusubtype: U32::new(endian, header.cpusubtype), filetype: U32::new(endian, header.filetype), ncmds: U32::new(endian, header.ncmds), sizeofcmds: U32::new(endian, header.sizeofcmds), flags: U32::new(endian, header.flags), reserved: U32::default(), }; buffer.write(&header); } fn write_segment_command(&self, buffer: &mut dyn WritableBuffer, segment: SegmentCommand) { let endian = self.endian; let segment = macho::SegmentCommand64 { cmd: U32::new(endian, macho::LC_SEGMENT_64), cmdsize: U32::new(endian, segment.cmdsize), segname: segment.segname, vmaddr: U64::new(endian, segment.vmaddr), vmsize: U64::new(endian, segment.vmsize), fileoff: U64::new(endian, segment.fileoff), filesize: U64::new(endian, segment.filesize), maxprot: U32::new(endian, segment.maxprot), initprot: U32::new(endian, segment.initprot), nsects: U32::new(endian, segment.nsects), flags: U32::new(endian, segment.flags), }; buffer.write(&segment); } fn write_section(&self, buffer: &mut dyn WritableBuffer, section: SectionHeader) { let endian = self.endian; let section = macho::Section64 { sectname: section.sectname, segname: section.segname, addr: U64::new(endian, section.addr), size: U64::new(endian, section.size), offset: U32::new(endian, section.offset), align: U32::new(endian, section.align), reloff: U32::new(endian, section.reloff), nreloc: U32::new(endian, section.nreloc), flags: U32::new(endian, section.flags), reserved1: U32::default(), reserved2: U32::default(), reserved3: U32::default(), }; buffer.write(§ion); } fn write_nlist(&self, buffer: &mut dyn WritableBuffer, nlist: Nlist) { let endian = self.endian; let nlist = macho::Nlist64 { n_strx: U32::new(endian, nlist.n_strx), n_type: nlist.n_type, n_sect: nlist.n_sect, n_desc: U16::new(endian, nlist.n_desc), n_value: U64Bytes::new(endian, nlist.n_value), }; buffer.write(&nlist); } } object-0.36.5/src/write/mod.rs000064400000000000000000001032231046102023000142340ustar 00000000000000//! Interface for writing object files. //! //! This module provides a unified write API for relocatable object files //! using [`Object`]. This does not support writing executable files. //! This supports the following file formats: COFF, ELF, Mach-O, and XCOFF. //! //! The submodules define helpers for writing the raw structs. These support //! writing both relocatable and executable files. There are writers for //! the following file formats: [COFF](coff::Writer), [ELF](elf::Writer), //! and [PE](pe::Writer). use alloc::borrow::Cow; use alloc::string::String; use alloc::vec::Vec; use core::{fmt, result, str}; #[cfg(not(feature = "std"))] use hashbrown::HashMap; #[cfg(feature = "std")] use std::{boxed::Box, collections::HashMap, error, io}; use crate::endian::{Endianness, U32, U64}; pub use crate::common::*; #[cfg(feature = "coff")] pub mod coff; #[cfg(feature = "coff")] pub use coff::CoffExportStyle; #[cfg(feature = "elf")] pub mod elf; #[cfg(feature = "macho")] mod macho; #[cfg(feature = "macho")] pub use macho::MachOBuildVersion; #[cfg(feature = "pe")] pub mod pe; #[cfg(feature = "xcoff")] mod xcoff; pub(crate) mod string; pub use string::StringId; mod util; pub use util::*; /// The error type used within the write module. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Error(pub(crate) String); impl fmt::Display for Error { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&self.0) } } #[cfg(feature = "std")] impl error::Error for Error {} /// The result type used within the write module. pub type Result = result::Result; /// A writable relocatable object file. #[derive(Debug)] pub struct Object<'a> { format: BinaryFormat, architecture: Architecture, sub_architecture: Option, endian: Endianness, sections: Vec>, standard_sections: HashMap, symbols: Vec, symbol_map: HashMap, SymbolId>, comdats: Vec, /// File flags that are specific to each file format. pub flags: FileFlags, /// The symbol name mangling scheme. pub mangling: Mangling, #[cfg(feature = "coff")] stub_symbols: HashMap, /// Mach-O "_tlv_bootstrap" symbol. #[cfg(feature = "macho")] tlv_bootstrap: Option, /// Mach-O CPU subtype. #[cfg(feature = "macho")] macho_cpu_subtype: Option, #[cfg(feature = "macho")] macho_build_version: Option, /// Mach-O MH_SUBSECTIONS_VIA_SYMBOLS flag. Only ever set if format is Mach-O. #[cfg(feature = "macho")] macho_subsections_via_symbols: bool, } impl<'a> Object<'a> { /// Create an empty object file. pub fn new(format: BinaryFormat, architecture: Architecture, endian: Endianness) -> Object<'a> { Object { format, architecture, sub_architecture: None, endian, sections: Vec::new(), standard_sections: HashMap::new(), symbols: Vec::new(), symbol_map: HashMap::new(), comdats: Vec::new(), flags: FileFlags::None, mangling: Mangling::default(format, architecture), #[cfg(feature = "coff")] stub_symbols: HashMap::new(), #[cfg(feature = "macho")] tlv_bootstrap: None, #[cfg(feature = "macho")] macho_cpu_subtype: None, #[cfg(feature = "macho")] macho_build_version: None, #[cfg(feature = "macho")] macho_subsections_via_symbols: false, } } /// Return the file format. #[inline] pub fn format(&self) -> BinaryFormat { self.format } /// Return the architecture. #[inline] pub fn architecture(&self) -> Architecture { self.architecture } /// Return the sub-architecture. #[inline] pub fn sub_architecture(&self) -> Option { self.sub_architecture } /// Specify the sub-architecture. pub fn set_sub_architecture(&mut self, sub_architecture: Option) { self.sub_architecture = sub_architecture; } /// Return the current mangling setting. #[inline] pub fn mangling(&self) -> Mangling { self.mangling } /// Specify the mangling setting. #[inline] pub fn set_mangling(&mut self, mangling: Mangling) { self.mangling = mangling; } /// Return the name for a standard segment. /// /// This will vary based on the file format. #[allow(unused_variables)] pub fn segment_name(&self, segment: StandardSegment) -> &'static [u8] { match self.format { #[cfg(feature = "coff")] BinaryFormat::Coff => &[], #[cfg(feature = "elf")] BinaryFormat::Elf => &[], #[cfg(feature = "macho")] BinaryFormat::MachO => self.macho_segment_name(segment), _ => unimplemented!(), } } /// Get the section with the given `SectionId`. #[inline] pub fn section(&self, section: SectionId) -> &Section<'a> { &self.sections[section.0] } /// Mutably get the section with the given `SectionId`. #[inline] pub fn section_mut(&mut self, section: SectionId) -> &mut Section<'a> { &mut self.sections[section.0] } /// Set the data for an existing section. /// /// Must not be called for sections that already have data, or that contain uninitialized data. /// `align` must be a power of two. pub fn set_section_data(&mut self, section: SectionId, data: T, align: u64) where T: Into>, { self.sections[section.0].set_data(data, align) } /// Append data to an existing section. Returns the section offset of the data. /// /// Must not be called for sections that contain uninitialized data. /// `align` must be a power of two. pub fn append_section_data(&mut self, section: SectionId, data: &[u8], align: u64) -> u64 { self.sections[section.0].append_data(data, align) } /// Append zero-initialized data to an existing section. Returns the section offset of the data. /// /// Must not be called for sections that contain initialized data. /// `align` must be a power of two. pub fn append_section_bss(&mut self, section: SectionId, size: u64, align: u64) -> u64 { self.sections[section.0].append_bss(size, align) } /// Return the `SectionId` of a standard section. /// /// If the section doesn't already exist then it is created. pub fn section_id(&mut self, section: StandardSection) -> SectionId { self.standard_sections .get(§ion) .cloned() .unwrap_or_else(|| { let (segment, name, kind, flags) = self.section_info(section); let id = self.add_section(segment.to_vec(), name.to_vec(), kind); self.section_mut(id).flags = flags; id }) } /// Add a new section and return its `SectionId`. /// /// This also creates a section symbol. pub fn add_section(&mut self, segment: Vec, name: Vec, kind: SectionKind) -> SectionId { let id = SectionId(self.sections.len()); self.sections.push(Section { segment, name, kind, size: 0, align: 1, data: Cow::Borrowed(&[]), relocations: Vec::new(), symbol: None, flags: SectionFlags::None, }); // Add to self.standard_sections if required. This may match multiple standard sections. let section = &self.sections[id.0]; for standard_section in StandardSection::all() { if !self.standard_sections.contains_key(standard_section) { let (segment, name, kind, _flags) = self.section_info(*standard_section); if segment == &*section.segment && name == &*section.name && kind == section.kind { self.standard_sections.insert(*standard_section, id); } } } id } fn section_info( &self, section: StandardSection, ) -> (&'static [u8], &'static [u8], SectionKind, SectionFlags) { match self.format { #[cfg(feature = "coff")] BinaryFormat::Coff => self.coff_section_info(section), #[cfg(feature = "elf")] BinaryFormat::Elf => self.elf_section_info(section), #[cfg(feature = "macho")] BinaryFormat::MachO => self.macho_section_info(section), #[cfg(feature = "xcoff")] BinaryFormat::Xcoff => self.xcoff_section_info(section), _ => unimplemented!(), } } /// Add a subsection. Returns the `SectionId` and section offset of the data. /// /// For Mach-O, this does not create a subsection, and instead uses the /// section from [`Self::section_id`]. Use [`Self::set_subsections_via_symbols`] /// to enable subsections via symbols. pub fn add_subsection(&mut self, section: StandardSection, name: &[u8]) -> SectionId { if self.has_subsections_via_symbols() { self.section_id(section) } else { let (segment, name, kind, flags) = self.subsection_info(section, name); let id = self.add_section(segment.to_vec(), name, kind); self.section_mut(id).flags = flags; id } } fn has_subsections_via_symbols(&self) -> bool { self.format == BinaryFormat::MachO } /// Enable subsections via symbols if supported. /// /// This should be called before adding any subsections or symbols. /// /// For Mach-O, this sets the `MH_SUBSECTIONS_VIA_SYMBOLS` flag. /// For other formats, this does nothing. pub fn set_subsections_via_symbols(&mut self) { #[cfg(feature = "macho")] if self.format == BinaryFormat::MachO { self.macho_subsections_via_symbols = true; } } fn subsection_info( &self, section: StandardSection, value: &[u8], ) -> (&'static [u8], Vec, SectionKind, SectionFlags) { let (segment, section, kind, flags) = self.section_info(section); let name = self.subsection_name(section, value); (segment, name, kind, flags) } #[allow(unused_variables)] fn subsection_name(&self, section: &[u8], value: &[u8]) -> Vec { debug_assert!(!self.has_subsections_via_symbols()); match self.format { #[cfg(feature = "coff")] BinaryFormat::Coff => self.coff_subsection_name(section, value), #[cfg(feature = "elf")] BinaryFormat::Elf => self.elf_subsection_name(section, value), _ => unimplemented!(), } } /// Get the COMDAT section group with the given `ComdatId`. #[inline] pub fn comdat(&self, comdat: ComdatId) -> &Comdat { &self.comdats[comdat.0] } /// Mutably get the COMDAT section group with the given `ComdatId`. #[inline] pub fn comdat_mut(&mut self, comdat: ComdatId) -> &mut Comdat { &mut self.comdats[comdat.0] } /// Add a new COMDAT section group and return its `ComdatId`. pub fn add_comdat(&mut self, comdat: Comdat) -> ComdatId { let comdat_id = ComdatId(self.comdats.len()); self.comdats.push(comdat); comdat_id } /// Get the `SymbolId` of the symbol with the given name. pub fn symbol_id(&self, name: &[u8]) -> Option { self.symbol_map.get(name).cloned() } /// Get the symbol with the given `SymbolId`. #[inline] pub fn symbol(&self, symbol: SymbolId) -> &Symbol { &self.symbols[symbol.0] } /// Mutably get the symbol with the given `SymbolId`. #[inline] pub fn symbol_mut(&mut self, symbol: SymbolId) -> &mut Symbol { &mut self.symbols[symbol.0] } /// Add a new symbol and return its `SymbolId`. pub fn add_symbol(&mut self, mut symbol: Symbol) -> SymbolId { // Defined symbols must have a scope. debug_assert!(symbol.is_undefined() || symbol.scope != SymbolScope::Unknown); if symbol.kind == SymbolKind::Section { // There can only be one section symbol, but update its flags, since // the automatically generated section symbol will have none. let symbol_id = self.section_symbol(symbol.section.id().unwrap()); if symbol.flags != SymbolFlags::None { self.symbol_mut(symbol_id).flags = symbol.flags; } return symbol_id; } if !symbol.name.is_empty() && (symbol.kind == SymbolKind::Text || symbol.kind == SymbolKind::Data || symbol.kind == SymbolKind::Tls) { let unmangled_name = symbol.name.clone(); if let Some(prefix) = self.mangling.global_prefix() { symbol.name.insert(0, prefix); } let symbol_id = self.add_raw_symbol(symbol); self.symbol_map.insert(unmangled_name, symbol_id); symbol_id } else { self.add_raw_symbol(symbol) } } fn add_raw_symbol(&mut self, symbol: Symbol) -> SymbolId { let symbol_id = SymbolId(self.symbols.len()); self.symbols.push(symbol); symbol_id } /// Return true if the file format supports `StandardSection::UninitializedTls`. #[inline] pub fn has_uninitialized_tls(&self) -> bool { self.format != BinaryFormat::Coff } /// Return true if the file format supports `StandardSection::Common`. #[inline] pub fn has_common(&self) -> bool { self.format == BinaryFormat::MachO } /// Add a new common symbol and return its `SymbolId`. /// /// For Mach-O, this appends the symbol to the `__common` section. /// /// `align` must be a power of two. pub fn add_common_symbol(&mut self, mut symbol: Symbol, size: u64, align: u64) -> SymbolId { if self.has_common() { let symbol_id = self.add_symbol(symbol); let section = self.section_id(StandardSection::Common); self.add_symbol_bss(symbol_id, section, size, align); symbol_id } else { symbol.section = SymbolSection::Common; symbol.size = size; self.add_symbol(symbol) } } /// Add a new file symbol and return its `SymbolId`. pub fn add_file_symbol(&mut self, name: Vec) -> SymbolId { self.add_raw_symbol(Symbol { name, value: 0, size: 0, kind: SymbolKind::File, scope: SymbolScope::Compilation, weak: false, section: SymbolSection::None, flags: SymbolFlags::None, }) } /// Get the symbol for a section. pub fn section_symbol(&mut self, section_id: SectionId) -> SymbolId { let section = &mut self.sections[section_id.0]; if let Some(symbol) = section.symbol { return symbol; } let name = if self.format == BinaryFormat::Coff { section.name.clone() } else { Vec::new() }; let symbol_id = SymbolId(self.symbols.len()); self.symbols.push(Symbol { name, value: 0, size: 0, kind: SymbolKind::Section, scope: SymbolScope::Compilation, weak: false, section: SymbolSection::Section(section_id), flags: SymbolFlags::None, }); section.symbol = Some(symbol_id); symbol_id } /// Append data to an existing section, and update a symbol to refer to it. /// /// For Mach-O, this also creates a `__thread_vars` entry for TLS symbols, and the /// symbol will indirectly point to the added data via the `__thread_vars` entry. /// /// For Mach-O, if [`Self::set_subsections_via_symbols`] is enabled, this will /// automatically ensure the data size is at least 1. /// /// Returns the section offset of the data. /// /// Must not be called for sections that contain uninitialized data. /// `align` must be a power of two. pub fn add_symbol_data( &mut self, symbol_id: SymbolId, section: SectionId, #[cfg_attr(not(feature = "macho"), allow(unused_mut))] mut data: &[u8], align: u64, ) -> u64 { #[cfg(feature = "macho")] if data.is_empty() && self.macho_subsections_via_symbols { data = &[0]; } let offset = self.append_section_data(section, data, align); self.set_symbol_data(symbol_id, section, offset, data.len() as u64); offset } /// Append zero-initialized data to an existing section, and update a symbol to refer to it. /// /// For Mach-O, this also creates a `__thread_vars` entry for TLS symbols, and the /// symbol will indirectly point to the added data via the `__thread_vars` entry. /// /// For Mach-O, if [`Self::set_subsections_via_symbols`] is enabled, this will /// automatically ensure the data size is at least 1. /// /// Returns the section offset of the data. /// /// Must not be called for sections that contain initialized data. /// `align` must be a power of two. pub fn add_symbol_bss( &mut self, symbol_id: SymbolId, section: SectionId, #[cfg_attr(not(feature = "macho"), allow(unused_mut))] mut size: u64, align: u64, ) -> u64 { #[cfg(feature = "macho")] if size == 0 && self.macho_subsections_via_symbols { size = 1; } let offset = self.append_section_bss(section, size, align); self.set_symbol_data(symbol_id, section, offset, size); offset } /// Update a symbol to refer to the given data within a section. /// /// For Mach-O, this also creates a `__thread_vars` entry for TLS symbols, and the /// symbol will indirectly point to the data via the `__thread_vars` entry. #[allow(unused_mut)] pub fn set_symbol_data( &mut self, mut symbol_id: SymbolId, section: SectionId, offset: u64, size: u64, ) { // Defined symbols must have a scope. debug_assert!(self.symbol(symbol_id).scope != SymbolScope::Unknown); match self.format { #[cfg(feature = "macho")] BinaryFormat::MachO => symbol_id = self.macho_add_thread_var(symbol_id), _ => {} } let symbol = self.symbol_mut(symbol_id); symbol.value = offset; symbol.size = size; symbol.section = SymbolSection::Section(section); } /// Convert a symbol to a section symbol and offset. /// /// Returns `None` if the symbol does not have a section. pub fn symbol_section_and_offset(&mut self, symbol_id: SymbolId) -> Option<(SymbolId, u64)> { let symbol = self.symbol(symbol_id); if symbol.kind == SymbolKind::Section { return Some((symbol_id, 0)); } let symbol_offset = symbol.value; let section = symbol.section.id()?; let section_symbol = self.section_symbol(section); Some((section_symbol, symbol_offset)) } /// Add a relocation to a section. /// /// Relocations must only be added after the referenced symbols have been added /// and defined (if applicable). pub fn add_relocation(&mut self, section: SectionId, mut relocation: Relocation) -> Result<()> { match self.format { #[cfg(feature = "coff")] BinaryFormat::Coff => self.coff_translate_relocation(&mut relocation)?, #[cfg(feature = "elf")] BinaryFormat::Elf => self.elf_translate_relocation(&mut relocation)?, #[cfg(feature = "macho")] BinaryFormat::MachO => self.macho_translate_relocation(&mut relocation)?, #[cfg(feature = "xcoff")] BinaryFormat::Xcoff => self.xcoff_translate_relocation(&mut relocation)?, _ => unimplemented!(), } let implicit = match self.format { #[cfg(feature = "coff")] BinaryFormat::Coff => self.coff_adjust_addend(&mut relocation)?, #[cfg(feature = "elf")] BinaryFormat::Elf => self.elf_adjust_addend(&mut relocation)?, #[cfg(feature = "macho")] BinaryFormat::MachO => self.macho_adjust_addend(&mut relocation)?, #[cfg(feature = "xcoff")] BinaryFormat::Xcoff => self.xcoff_adjust_addend(&mut relocation)?, _ => unimplemented!(), }; if implicit && relocation.addend != 0 { self.write_relocation_addend(section, &relocation)?; relocation.addend = 0; } self.sections[section.0].relocations.push(relocation); Ok(()) } fn write_relocation_addend( &mut self, section: SectionId, relocation: &Relocation, ) -> Result<()> { let size = match self.format { #[cfg(feature = "coff")] BinaryFormat::Coff => self.coff_relocation_size(relocation)?, #[cfg(feature = "elf")] BinaryFormat::Elf => self.elf_relocation_size(relocation)?, #[cfg(feature = "macho")] BinaryFormat::MachO => self.macho_relocation_size(relocation)?, #[cfg(feature = "xcoff")] BinaryFormat::Xcoff => self.xcoff_relocation_size(relocation)?, _ => unimplemented!(), }; let data = self.sections[section.0].data_mut(); let offset = relocation.offset as usize; match size { 32 => data.write_at(offset, &U32::new(self.endian, relocation.addend as u32)), 64 => data.write_at(offset, &U64::new(self.endian, relocation.addend as u64)), _ => { return Err(Error(format!( "unimplemented relocation addend {:?}", relocation ))); } } .map_err(|_| { Error(format!( "invalid relocation offset {}+{} (max {})", relocation.offset, size, data.len() )) }) } /// Write the object to a `Vec`. pub fn write(&self) -> Result> { let mut buffer = Vec::new(); self.emit(&mut buffer)?; Ok(buffer) } /// Write the object to a `Write` implementation. /// /// Also flushes the writer. /// /// It is advisable to use a buffered writer like [`BufWriter`](std::io::BufWriter) /// instead of an unbuffered writer like [`File`](std::fs::File). #[cfg(feature = "std")] pub fn write_stream(&self, w: W) -> result::Result<(), Box> { let mut stream = StreamingBuffer::new(w); self.emit(&mut stream)?; stream.result()?; stream.into_inner().flush()?; Ok(()) } /// Write the object to a `WritableBuffer`. pub fn emit(&self, buffer: &mut dyn WritableBuffer) -> Result<()> { match self.format { #[cfg(feature = "coff")] BinaryFormat::Coff => self.coff_write(buffer), #[cfg(feature = "elf")] BinaryFormat::Elf => self.elf_write(buffer), #[cfg(feature = "macho")] BinaryFormat::MachO => self.macho_write(buffer), #[cfg(feature = "xcoff")] BinaryFormat::Xcoff => self.xcoff_write(buffer), _ => unimplemented!(), } } } /// A standard segment kind. #[allow(missing_docs)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[non_exhaustive] pub enum StandardSegment { Text, Data, Debug, } /// A standard section kind. #[allow(missing_docs)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[non_exhaustive] pub enum StandardSection { Text, Data, ReadOnlyData, ReadOnlyDataWithRel, ReadOnlyString, UninitializedData, Tls, /// Zero-fill TLS initializers. Unsupported for COFF. UninitializedTls, /// TLS variable structures. Only supported for Mach-O. TlsVariables, /// Common data. Only supported for Mach-O. Common, /// Notes for GNU properties. Only supported for ELF. GnuProperty, } impl StandardSection { /// Return the section kind of a standard section. pub fn kind(self) -> SectionKind { match self { StandardSection::Text => SectionKind::Text, StandardSection::Data => SectionKind::Data, StandardSection::ReadOnlyData => SectionKind::ReadOnlyData, StandardSection::ReadOnlyDataWithRel => SectionKind::ReadOnlyDataWithRel, StandardSection::ReadOnlyString => SectionKind::ReadOnlyString, StandardSection::UninitializedData => SectionKind::UninitializedData, StandardSection::Tls => SectionKind::Tls, StandardSection::UninitializedTls => SectionKind::UninitializedTls, StandardSection::TlsVariables => SectionKind::TlsVariables, StandardSection::Common => SectionKind::Common, StandardSection::GnuProperty => SectionKind::Note, } } // TODO: remembering to update this is error-prone, can we do better? fn all() -> &'static [StandardSection] { &[ StandardSection::Text, StandardSection::Data, StandardSection::ReadOnlyData, StandardSection::ReadOnlyDataWithRel, StandardSection::ReadOnlyString, StandardSection::UninitializedData, StandardSection::Tls, StandardSection::UninitializedTls, StandardSection::TlsVariables, StandardSection::Common, StandardSection::GnuProperty, ] } } /// An identifier used to reference a section. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SectionId(usize); /// A section in an object file. #[derive(Debug)] pub struct Section<'a> { segment: Vec, name: Vec, kind: SectionKind, size: u64, align: u64, data: Cow<'a, [u8]>, relocations: Vec, symbol: Option, /// Section flags that are specific to each file format. pub flags: SectionFlags, } impl<'a> Section<'a> { /// Try to convert the name to a utf8 string. #[inline] pub fn name(&self) -> Option<&str> { str::from_utf8(&self.name).ok() } /// Try to convert the segment to a utf8 string. #[inline] pub fn segment(&self) -> Option<&str> { str::from_utf8(&self.segment).ok() } /// Return true if this section contains zerofill data. #[inline] pub fn is_bss(&self) -> bool { self.kind.is_bss() } /// Set the data for a section. /// /// Must not be called for sections that already have data, or that contain uninitialized data. /// `align` must be a power of two. pub fn set_data(&mut self, data: T, align: u64) where T: Into>, { debug_assert!(!self.is_bss()); debug_assert_eq!(align & (align - 1), 0); debug_assert!(self.data.is_empty()); self.data = data.into(); self.size = self.data.len() as u64; self.align = align; } /// Append data to a section. /// /// Must not be called for sections that contain uninitialized data. /// `align` must be a power of two. pub fn append_data(&mut self, append_data: &[u8], align: u64) -> u64 { debug_assert!(!self.is_bss()); debug_assert_eq!(align & (align - 1), 0); if self.align < align { self.align = align; } let align = align as usize; let data = self.data.to_mut(); let mut offset = data.len(); if offset & (align - 1) != 0 { offset += align - (offset & (align - 1)); data.resize(offset, 0); } data.extend_from_slice(append_data); self.size = data.len() as u64; offset as u64 } /// Append uninitialized data to a section. /// /// Must not be called for sections that contain initialized data. /// `align` must be a power of two. pub fn append_bss(&mut self, size: u64, align: u64) -> u64 { debug_assert!(self.is_bss()); debug_assert_eq!(align & (align - 1), 0); if self.align < align { self.align = align; } let mut offset = self.size; if offset & (align - 1) != 0 { offset += align - (offset & (align - 1)); self.size = offset; } self.size += size; offset } /// Returns the section as-built so far. /// /// This requires that the section is not a bss section. pub fn data(&self) -> &[u8] { debug_assert!(!self.is_bss()); &self.data } /// Returns the section as-built so far. /// /// This requires that the section is not a bss section. pub fn data_mut(&mut self) -> &mut [u8] { debug_assert!(!self.is_bss()); self.data.to_mut() } } /// The section where a symbol is defined. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum SymbolSection { /// The section is not applicable for this symbol (such as file symbols). None, /// The symbol is undefined. Undefined, /// The symbol has an absolute value. Absolute, /// The symbol is a zero-initialized symbol that will be combined with duplicate definitions. Common, /// The symbol is defined in the given section. Section(SectionId), } impl SymbolSection { /// Returns the section id for the section where the symbol is defined. /// /// May return `None` if the symbol is not defined in a section. #[inline] pub fn id(self) -> Option { if let SymbolSection::Section(id) = self { Some(id) } else { None } } } /// An identifier used to reference a symbol. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SymbolId(usize); /// A symbol in an object file. #[derive(Debug)] pub struct Symbol { /// The name of the symbol. pub name: Vec, /// The value of the symbol. /// /// If the symbol defined in a section, then this is the section offset of the symbol. pub value: u64, /// The size of the symbol. pub size: u64, /// The kind of the symbol. pub kind: SymbolKind, /// The scope of the symbol. pub scope: SymbolScope, /// Whether the symbol has weak binding. pub weak: bool, /// The section containing the symbol. pub section: SymbolSection, /// Symbol flags that are specific to each file format. pub flags: SymbolFlags, } impl Symbol { /// Try to convert the name to a utf8 string. #[inline] pub fn name(&self) -> Option<&str> { str::from_utf8(&self.name).ok() } /// Return true if the symbol is undefined. #[inline] pub fn is_undefined(&self) -> bool { self.section == SymbolSection::Undefined } /// Return true if the symbol is common data. /// /// Note: does not check for `SymbolSection::Section` with `SectionKind::Common`. #[inline] pub fn is_common(&self) -> bool { self.section == SymbolSection::Common } /// Return true if the symbol scope is local. #[inline] pub fn is_local(&self) -> bool { self.scope == SymbolScope::Compilation } } /// A relocation in an object file. #[derive(Debug)] pub struct Relocation { /// The section offset of the place of the relocation. pub offset: u64, /// The symbol referred to by the relocation. /// /// This may be a section symbol. pub symbol: SymbolId, /// The addend to use in the relocation calculation. /// /// This may be in addition to an implicit addend stored at the place of the relocation. pub addend: i64, /// The fields that define the relocation type. pub flags: RelocationFlags, } /// An identifier used to reference a COMDAT section group. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ComdatId(usize); /// A COMDAT section group. #[derive(Debug)] pub struct Comdat { /// The COMDAT selection kind. /// /// This determines the way in which the linker resolves multiple definitions of the COMDAT /// sections. pub kind: ComdatKind, /// The COMDAT symbol. /// /// If this symbol is referenced, then all sections in the group will be included by the /// linker. pub symbol: SymbolId, /// The sections in the group. pub sections: Vec, } /// The symbol name mangling scheme. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum Mangling { /// No symbol mangling. None, /// Windows COFF symbol mangling. Coff, /// Windows COFF i386 symbol mangling. CoffI386, /// ELF symbol mangling. Elf, /// Mach-O symbol mangling. MachO, /// Xcoff symbol mangling. Xcoff, } impl Mangling { /// Return the default symboling mangling for the given format and architecture. pub fn default(format: BinaryFormat, architecture: Architecture) -> Self { match (format, architecture) { (BinaryFormat::Coff, Architecture::I386) => Mangling::CoffI386, (BinaryFormat::Coff, _) => Mangling::Coff, (BinaryFormat::Elf, _) => Mangling::Elf, (BinaryFormat::MachO, _) => Mangling::MachO, (BinaryFormat::Xcoff, _) => Mangling::Xcoff, _ => Mangling::None, } } /// Return the prefix to use for global symbols. pub fn global_prefix(self) -> Option { match self { Mangling::None | Mangling::Elf | Mangling::Coff | Mangling::Xcoff => None, Mangling::CoffI386 | Mangling::MachO => Some(b'_'), } } } object-0.36.5/src/write/pe.rs000064400000000000000000000736371046102023000141000ustar 00000000000000//! Helper for writing PE files. use alloc::string::String; use alloc::vec::Vec; use core::mem; use crate::endian::{LittleEndian as LE, *}; use crate::pe; use crate::write::util; use crate::write::{Error, Result, WritableBuffer}; /// A helper for writing PE files. /// /// Writing uses a two phase approach. The first phase reserves file ranges and virtual /// address ranges for everything in the order that they will be written. /// /// The second phase writes everything out in order. Thus the caller must ensure writing /// is in the same order that file ranges were reserved. #[allow(missing_debug_implementations)] pub struct Writer<'a> { is_64: bool, section_alignment: u32, file_alignment: u32, buffer: &'a mut dyn WritableBuffer, len: u32, virtual_len: u32, headers_len: u32, code_address: u32, data_address: u32, code_len: u32, data_len: u32, bss_len: u32, nt_headers_offset: u32, data_directories: Vec, section_header_num: u16, sections: Vec
, symbol_offset: u32, symbol_num: u32, reloc_blocks: Vec, relocs: Vec>, reloc_offset: u32, } impl<'a> Writer<'a> { /// Create a new `Writer`. /// /// The alignment values must be powers of two. pub fn new( is_64: bool, section_alignment: u32, file_alignment: u32, buffer: &'a mut dyn WritableBuffer, ) -> Self { Writer { is_64, section_alignment, file_alignment, buffer, len: 0, virtual_len: 0, headers_len: 0, code_address: 0, data_address: 0, code_len: 0, data_len: 0, bss_len: 0, nt_headers_offset: 0, data_directories: Vec::new(), section_header_num: 0, sections: Vec::new(), symbol_offset: 0, symbol_num: 0, reloc_blocks: Vec::new(), relocs: Vec::new(), reloc_offset: 0, } } /// Return the current virtual address size that has been reserved. /// /// This is only valid after section headers have been reserved. pub fn virtual_len(&self) -> u32 { self.virtual_len } /// Reserve a virtual address range with the given size. /// /// The reserved length will be increased to match the section alignment. /// /// Returns the aligned offset of the start of the range. pub fn reserve_virtual(&mut self, len: u32) -> u32 { let offset = self.virtual_len; self.virtual_len += len; self.virtual_len = util::align_u32(self.virtual_len, self.section_alignment); offset } /// Reserve up to the given virtual address. /// /// The reserved length will be increased to match the section alignment. pub fn reserve_virtual_until(&mut self, address: u32) { debug_assert!(self.virtual_len <= address); self.virtual_len = util::align_u32(address, self.section_alignment); } /// Return the current file length that has been reserved. pub fn reserved_len(&self) -> u32 { self.len } /// Return the current file length that has been written. #[allow(clippy::len_without_is_empty)] pub fn len(&self) -> usize { self.buffer.len() } /// Reserve a file range with the given size and starting alignment. /// /// Returns the aligned offset of the start of the range. pub fn reserve(&mut self, len: u32, align_start: u32) -> u32 { if len == 0 { return self.len; } self.reserve_align(align_start); let offset = self.len; self.len += len; offset } /// Reserve a file range with the given size and using the file alignment. /// /// Returns the aligned offset of the start of the range. pub fn reserve_file(&mut self, len: u32) -> u32 { self.reserve(len, self.file_alignment) } /// Write data. pub fn write(&mut self, data: &[u8]) { self.buffer.write_bytes(data); } /// Reserve alignment padding bytes. pub fn reserve_align(&mut self, align_start: u32) { self.len = util::align_u32(self.len, align_start); } /// Write alignment padding bytes. pub fn write_align(&mut self, align_start: u32) { util::write_align(self.buffer, align_start as usize); } /// Write padding up to the next multiple of file alignment. pub fn write_file_align(&mut self) { self.write_align(self.file_alignment); } /// Reserve the file range up to the given file offset. pub fn reserve_until(&mut self, offset: u32) { debug_assert!(self.len <= offset); self.len = offset; } /// Write padding up to the given file offset. pub fn pad_until(&mut self, offset: u32) { debug_assert!(self.buffer.len() <= offset as usize); self.buffer.resize(offset as usize); } /// Reserve the range for the DOS header. /// /// This must be at the start of the file. /// /// When writing, you may use `write_custom_dos_header` or `write_empty_dos_header`. pub fn reserve_dos_header(&mut self) { debug_assert_eq!(self.len, 0); self.reserve(mem::size_of::() as u32, 1); } /// Write a custom DOS header. /// /// This must be at the start of the file. pub fn write_custom_dos_header(&mut self, dos_header: &pe::ImageDosHeader) -> Result<()> { debug_assert_eq!(self.buffer.len(), 0); // Start writing. self.buffer .reserve(self.len as usize) .map_err(|_| Error(String::from("Cannot allocate buffer")))?; self.buffer.write(dos_header); Ok(()) } /// Write the DOS header for a file without a stub. /// /// This must be at the start of the file. /// /// Uses default values for all fields. pub fn write_empty_dos_header(&mut self) -> Result<()> { self.write_custom_dos_header(&pe::ImageDosHeader { e_magic: U16::new(LE, pe::IMAGE_DOS_SIGNATURE), e_cblp: U16::new(LE, 0), e_cp: U16::new(LE, 0), e_crlc: U16::new(LE, 0), e_cparhdr: U16::new(LE, 0), e_minalloc: U16::new(LE, 0), e_maxalloc: U16::new(LE, 0), e_ss: U16::new(LE, 0), e_sp: U16::new(LE, 0), e_csum: U16::new(LE, 0), e_ip: U16::new(LE, 0), e_cs: U16::new(LE, 0), e_lfarlc: U16::new(LE, 0), e_ovno: U16::new(LE, 0), e_res: [U16::new(LE, 0); 4], e_oemid: U16::new(LE, 0), e_oeminfo: U16::new(LE, 0), e_res2: [U16::new(LE, 0); 10], e_lfanew: U32::new(LE, self.nt_headers_offset), }) } /// Reserve a fixed DOS header and stub. /// /// Use `reserve_dos_header` and `reserve` if you need a custom stub. pub fn reserve_dos_header_and_stub(&mut self) { self.reserve_dos_header(); self.reserve(64, 1); } /// Write a fixed DOS header and stub. /// /// Use `write_custom_dos_header` and `write` if you need a custom stub. pub fn write_dos_header_and_stub(&mut self) -> Result<()> { self.write_custom_dos_header(&pe::ImageDosHeader { e_magic: U16::new(LE, pe::IMAGE_DOS_SIGNATURE), e_cblp: U16::new(LE, 0x90), e_cp: U16::new(LE, 3), e_crlc: U16::new(LE, 0), e_cparhdr: U16::new(LE, 4), e_minalloc: U16::new(LE, 0), e_maxalloc: U16::new(LE, 0xffff), e_ss: U16::new(LE, 0), e_sp: U16::new(LE, 0xb8), e_csum: U16::new(LE, 0), e_ip: U16::new(LE, 0), e_cs: U16::new(LE, 0), e_lfarlc: U16::new(LE, 0x40), e_ovno: U16::new(LE, 0), e_res: [U16::new(LE, 0); 4], e_oemid: U16::new(LE, 0), e_oeminfo: U16::new(LE, 0), e_res2: [U16::new(LE, 0); 10], e_lfanew: U32::new(LE, self.nt_headers_offset), })?; #[rustfmt::skip] self.buffer.write_bytes(&[ 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x0d, 0x0d, 0x0a, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); Ok(()) } fn nt_headers_size(&self) -> u32 { if self.is_64 { mem::size_of::() as u32 } else { mem::size_of::() as u32 } } fn optional_header_size(&self) -> u32 { let size = if self.is_64 { mem::size_of::() as u32 } else { mem::size_of::() as u32 }; size + self.data_directories.len() as u32 * mem::size_of::() as u32 } /// Return the offset of the NT headers, if reserved. pub fn nt_headers_offset(&self) -> u32 { self.nt_headers_offset } /// Reserve the range for the NT headers. pub fn reserve_nt_headers(&mut self, data_directory_num: usize) { debug_assert_eq!(self.nt_headers_offset, 0); self.nt_headers_offset = self.reserve(self.nt_headers_size(), 8); self.data_directories = vec![DataDirectory::default(); data_directory_num]; self.reserve( data_directory_num as u32 * mem::size_of::() as u32, 1, ); } /// Set the virtual address and size of a data directory. pub fn set_data_directory(&mut self, index: usize, virtual_address: u32, size: u32) { self.data_directories[index] = DataDirectory { virtual_address, size, } } /// Write the NT headers. pub fn write_nt_headers(&mut self, nt_headers: NtHeaders) { self.pad_until(self.nt_headers_offset); self.buffer.write(&U32::new(LE, pe::IMAGE_NT_SIGNATURE)); let file_header = pe::ImageFileHeader { machine: U16::new(LE, nt_headers.machine), number_of_sections: U16::new(LE, self.section_header_num), time_date_stamp: U32::new(LE, nt_headers.time_date_stamp), pointer_to_symbol_table: U32::new(LE, self.symbol_offset), number_of_symbols: U32::new(LE, self.symbol_num), size_of_optional_header: U16::new(LE, self.optional_header_size() as u16), characteristics: U16::new(LE, nt_headers.characteristics), }; self.buffer.write(&file_header); if self.is_64 { let optional_header = pe::ImageOptionalHeader64 { magic: U16::new(LE, pe::IMAGE_NT_OPTIONAL_HDR64_MAGIC), major_linker_version: nt_headers.major_linker_version, minor_linker_version: nt_headers.minor_linker_version, size_of_code: U32::new(LE, self.code_len), size_of_initialized_data: U32::new(LE, self.data_len), size_of_uninitialized_data: U32::new(LE, self.bss_len), address_of_entry_point: U32::new(LE, nt_headers.address_of_entry_point), base_of_code: U32::new(LE, self.code_address), image_base: U64::new(LE, nt_headers.image_base), section_alignment: U32::new(LE, self.section_alignment), file_alignment: U32::new(LE, self.file_alignment), major_operating_system_version: U16::new( LE, nt_headers.major_operating_system_version, ), minor_operating_system_version: U16::new( LE, nt_headers.minor_operating_system_version, ), major_image_version: U16::new(LE, nt_headers.major_image_version), minor_image_version: U16::new(LE, nt_headers.minor_image_version), major_subsystem_version: U16::new(LE, nt_headers.major_subsystem_version), minor_subsystem_version: U16::new(LE, nt_headers.minor_subsystem_version), win32_version_value: U32::new(LE, 0), size_of_image: U32::new(LE, self.virtual_len), size_of_headers: U32::new(LE, self.headers_len), check_sum: U32::new(LE, 0), subsystem: U16::new(LE, nt_headers.subsystem), dll_characteristics: U16::new(LE, nt_headers.dll_characteristics), size_of_stack_reserve: U64::new(LE, nt_headers.size_of_stack_reserve), size_of_stack_commit: U64::new(LE, nt_headers.size_of_stack_commit), size_of_heap_reserve: U64::new(LE, nt_headers.size_of_heap_reserve), size_of_heap_commit: U64::new(LE, nt_headers.size_of_heap_commit), loader_flags: U32::new(LE, 0), number_of_rva_and_sizes: U32::new(LE, self.data_directories.len() as u32), }; self.buffer.write(&optional_header); } else { let optional_header = pe::ImageOptionalHeader32 { magic: U16::new(LE, pe::IMAGE_NT_OPTIONAL_HDR32_MAGIC), major_linker_version: nt_headers.major_linker_version, minor_linker_version: nt_headers.minor_linker_version, size_of_code: U32::new(LE, self.code_len), size_of_initialized_data: U32::new(LE, self.data_len), size_of_uninitialized_data: U32::new(LE, self.bss_len), address_of_entry_point: U32::new(LE, nt_headers.address_of_entry_point), base_of_code: U32::new(LE, self.code_address), base_of_data: U32::new(LE, self.data_address), image_base: U32::new(LE, nt_headers.image_base as u32), section_alignment: U32::new(LE, self.section_alignment), file_alignment: U32::new(LE, self.file_alignment), major_operating_system_version: U16::new( LE, nt_headers.major_operating_system_version, ), minor_operating_system_version: U16::new( LE, nt_headers.minor_operating_system_version, ), major_image_version: U16::new(LE, nt_headers.major_image_version), minor_image_version: U16::new(LE, nt_headers.minor_image_version), major_subsystem_version: U16::new(LE, nt_headers.major_subsystem_version), minor_subsystem_version: U16::new(LE, nt_headers.minor_subsystem_version), win32_version_value: U32::new(LE, 0), size_of_image: U32::new(LE, self.virtual_len), size_of_headers: U32::new(LE, self.headers_len), check_sum: U32::new(LE, 0), subsystem: U16::new(LE, nt_headers.subsystem), dll_characteristics: U16::new(LE, nt_headers.dll_characteristics), size_of_stack_reserve: U32::new(LE, nt_headers.size_of_stack_reserve as u32), size_of_stack_commit: U32::new(LE, nt_headers.size_of_stack_commit as u32), size_of_heap_reserve: U32::new(LE, nt_headers.size_of_heap_reserve as u32), size_of_heap_commit: U32::new(LE, nt_headers.size_of_heap_commit as u32), loader_flags: U32::new(LE, 0), number_of_rva_and_sizes: U32::new(LE, self.data_directories.len() as u32), }; self.buffer.write(&optional_header); } for dir in &self.data_directories { self.buffer.write(&pe::ImageDataDirectory { virtual_address: U32::new(LE, dir.virtual_address), size: U32::new(LE, dir.size), }) } } /// Reserve the section headers. /// /// The number of reserved section headers must be the same as the number of sections that /// are later reserved. // TODO: change this to a maximum number of sections? pub fn reserve_section_headers(&mut self, section_header_num: u16) { debug_assert_eq!(self.section_header_num, 0); self.section_header_num = section_header_num; self.reserve( u32::from(section_header_num) * mem::size_of::() as u32, 1, ); // Padding before sections must be included in headers_len. self.reserve_align(self.file_alignment); self.headers_len = self.len; self.reserve_virtual(self.len); } /// Write the section headers. /// /// This uses information that was recorded when the sections were reserved. pub fn write_section_headers(&mut self) { debug_assert_eq!(self.section_header_num as usize, self.sections.len()); for section in &self.sections { let section_header = pe::ImageSectionHeader { name: section.name, virtual_size: U32::new(LE, section.range.virtual_size), virtual_address: U32::new(LE, section.range.virtual_address), size_of_raw_data: U32::new(LE, section.range.file_size), pointer_to_raw_data: U32::new(LE, section.range.file_offset), pointer_to_relocations: U32::new(LE, 0), pointer_to_linenumbers: U32::new(LE, 0), number_of_relocations: U16::new(LE, 0), number_of_linenumbers: U16::new(LE, 0), characteristics: U32::new(LE, section.characteristics), }; self.buffer.write(§ion_header); } } /// Reserve a section. /// /// Returns the file range and virtual address range that are reserved /// for the section. pub fn reserve_section( &mut self, name: [u8; 8], characteristics: u32, virtual_size: u32, data_size: u32, ) -> SectionRange { let virtual_address = self.reserve_virtual(virtual_size); // Padding after section must be included in section file size. let file_size = util::align_u32(data_size, self.file_alignment); let file_offset = if file_size != 0 { self.reserve(file_size, self.file_alignment) } else { 0 }; // Sizes in optional header use the virtual size with the file alignment. let aligned_virtual_size = util::align_u32(virtual_size, self.file_alignment); if characteristics & pe::IMAGE_SCN_CNT_CODE != 0 { if self.code_address == 0 { self.code_address = virtual_address; } self.code_len += aligned_virtual_size; } else if characteristics & pe::IMAGE_SCN_CNT_INITIALIZED_DATA != 0 { if self.data_address == 0 { self.data_address = virtual_address; } self.data_len += aligned_virtual_size; } else if characteristics & pe::IMAGE_SCN_CNT_UNINITIALIZED_DATA != 0 { if self.data_address == 0 { self.data_address = virtual_address; } self.bss_len += aligned_virtual_size; } let range = SectionRange { virtual_address, virtual_size, file_offset, file_size, }; self.sections.push(Section { name, characteristics, range, }); range } /// Write the data for a section. pub fn write_section(&mut self, offset: u32, data: &[u8]) { if data.is_empty() { return; } self.pad_until(offset); self.write(data); self.write_align(self.file_alignment); } /// Reserve a `.text` section. /// /// Contains executable code. pub fn reserve_text_section(&mut self, size: u32) -> SectionRange { self.reserve_section( *b".text\0\0\0", pe::IMAGE_SCN_CNT_CODE | pe::IMAGE_SCN_MEM_EXECUTE | pe::IMAGE_SCN_MEM_READ, size, size, ) } /// Reserve a `.data` section. /// /// Contains initialized data. /// /// May also contain uninitialized data if `virtual_size` is greater than `data_size`. pub fn reserve_data_section(&mut self, virtual_size: u32, data_size: u32) -> SectionRange { self.reserve_section( *b".data\0\0\0", pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ | pe::IMAGE_SCN_MEM_WRITE, virtual_size, data_size, ) } /// Reserve a `.rdata` section. /// /// Contains read-only initialized data. pub fn reserve_rdata_section(&mut self, size: u32) -> SectionRange { self.reserve_section( *b".rdata\0\0", pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ, size, size, ) } /// Reserve a `.bss` section. /// /// Contains uninitialized data. pub fn reserve_bss_section(&mut self, size: u32) -> SectionRange { self.reserve_section( *b".bss\0\0\0\0", pe::IMAGE_SCN_CNT_UNINITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ | pe::IMAGE_SCN_MEM_WRITE, size, 0, ) } /// Reserve an `.idata` section. /// /// Contains import tables. Note that it is permissible to store import tables in a different /// section. /// /// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_IMPORT` data directory. pub fn reserve_idata_section(&mut self, size: u32) -> SectionRange { let range = self.reserve_section( *b".idata\0\0", pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ | pe::IMAGE_SCN_MEM_WRITE, size, size, ); let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_IMPORT]; debug_assert_eq!(dir.virtual_address, 0); *dir = DataDirectory { virtual_address: range.virtual_address, size, }; range } /// Reserve an `.edata` section. /// /// Contains export tables. /// /// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_EXPORT` data directory. pub fn reserve_edata_section(&mut self, size: u32) -> SectionRange { let range = self.reserve_section( *b".edata\0\0", pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ, size, size, ); let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_EXPORT]; debug_assert_eq!(dir.virtual_address, 0); *dir = DataDirectory { virtual_address: range.virtual_address, size, }; range } /// Reserve a `.pdata` section. /// /// Contains exception information. /// /// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_EXCEPTION` data directory. pub fn reserve_pdata_section(&mut self, size: u32) -> SectionRange { let range = self.reserve_section( *b".pdata\0\0", pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ, size, size, ); let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_EXCEPTION]; debug_assert_eq!(dir.virtual_address, 0); *dir = DataDirectory { virtual_address: range.virtual_address, size, }; range } /// Reserve a `.xdata` section. /// /// Contains exception information. pub fn reserve_xdata_section(&mut self, size: u32) -> SectionRange { self.reserve_section( *b".xdata\0\0", pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ, size, size, ) } /// Reserve a `.rsrc` section. /// /// Contains the resource directory. /// /// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_RESOURCE` data directory. pub fn reserve_rsrc_section(&mut self, size: u32) -> SectionRange { let range = self.reserve_section( *b".rsrc\0\0\0", pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ, size, size, ); let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_RESOURCE]; debug_assert_eq!(dir.virtual_address, 0); *dir = DataDirectory { virtual_address: range.virtual_address, size, }; range } /// Add a base relocation. /// /// `typ` must be one of the `IMAGE_REL_BASED_*` constants. pub fn add_reloc(&mut self, mut virtual_address: u32, typ: u16) { let reloc = U16::new(LE, typ << 12 | (virtual_address & 0xfff) as u16); virtual_address &= !0xfff; if let Some(block) = self.reloc_blocks.last_mut() { if block.virtual_address == virtual_address { self.relocs.push(reloc); block.count += 1; return; } // Blocks must have an even number of relocations. if block.count & 1 != 0 { self.relocs.push(U16::new(LE, 0)); block.count += 1; } debug_assert!(block.virtual_address < virtual_address); } self.relocs.push(reloc); self.reloc_blocks.push(RelocBlock { virtual_address, count: 1, }); } /// Return true if a base relocation has been added. pub fn has_relocs(&mut self) -> bool { !self.relocs.is_empty() } /// Reserve a `.reloc` section. /// /// This contains the base relocations that were added with `add_reloc`. /// /// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_BASERELOC` data directory. pub fn reserve_reloc_section(&mut self) -> SectionRange { if let Some(block) = self.reloc_blocks.last_mut() { // Blocks must have an even number of relocations. if block.count & 1 != 0 { self.relocs.push(U16::new(LE, 0)); block.count += 1; } } let size = self.reloc_blocks.iter().map(RelocBlock::size).sum(); let range = self.reserve_section( *b".reloc\0\0", pe::IMAGE_SCN_CNT_INITIALIZED_DATA | pe::IMAGE_SCN_MEM_READ | pe::IMAGE_SCN_MEM_DISCARDABLE, size, size, ); let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_BASERELOC]; debug_assert_eq!(dir.virtual_address, 0); *dir = DataDirectory { virtual_address: range.virtual_address, size, }; self.reloc_offset = range.file_offset; range } /// Write a `.reloc` section. /// /// This contains the base relocations that were added with `add_reloc`. pub fn write_reloc_section(&mut self) { if self.reloc_offset == 0 { return; } self.pad_until(self.reloc_offset); let mut total = 0; for block in &self.reloc_blocks { self.buffer.write(&pe::ImageBaseRelocation { virtual_address: U32::new(LE, block.virtual_address), size_of_block: U32::new(LE, block.size()), }); self.buffer .write_slice(&self.relocs[total..][..block.count as usize]); total += block.count as usize; } debug_assert_eq!(total, self.relocs.len()); self.write_align(self.file_alignment); } /// Reserve the certificate table. /// /// This also sets the `pe::IMAGE_DIRECTORY_ENTRY_SECURITY` data directory. // TODO: reserve individual certificates pub fn reserve_certificate_table(&mut self, size: u32) { let size = util::align_u32(size, 8); let offset = self.reserve(size, 8); let dir = &mut self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_SECURITY]; debug_assert_eq!(dir.virtual_address, 0); *dir = DataDirectory { virtual_address: offset, size, }; } /// Write the certificate table. // TODO: write individual certificates pub fn write_certificate_table(&mut self, data: &[u8]) { let dir = self.data_directories[pe::IMAGE_DIRECTORY_ENTRY_SECURITY]; self.pad_until(dir.virtual_address); self.write(data); self.pad_until(dir.virtual_address + dir.size); } } /// Information required for writing [`pe::ImageNtHeaders32`] or [`pe::ImageNtHeaders64`]. #[allow(missing_docs)] #[derive(Debug, Clone)] pub struct NtHeaders { // ImageFileHeader pub machine: u16, pub time_date_stamp: u32, pub characteristics: u16, // ImageOptionalHeader pub major_linker_version: u8, pub minor_linker_version: u8, pub address_of_entry_point: u32, pub image_base: u64, pub major_operating_system_version: u16, pub minor_operating_system_version: u16, pub major_image_version: u16, pub minor_image_version: u16, pub major_subsystem_version: u16, pub minor_subsystem_version: u16, pub subsystem: u16, pub dll_characteristics: u16, pub size_of_stack_reserve: u64, pub size_of_stack_commit: u64, pub size_of_heap_reserve: u64, pub size_of_heap_commit: u64, } #[derive(Default, Clone, Copy)] struct DataDirectory { virtual_address: u32, size: u32, } /// Information required for writing [`pe::ImageSectionHeader`]. #[allow(missing_docs)] #[derive(Debug, Clone)] pub struct Section { pub name: [u8; pe::IMAGE_SIZEOF_SHORT_NAME], pub characteristics: u32, pub range: SectionRange, } /// The file range and virtual address range for a section. #[allow(missing_docs)] #[derive(Debug, Default, Clone, Copy)] pub struct SectionRange { pub virtual_address: u32, pub virtual_size: u32, pub file_offset: u32, pub file_size: u32, } struct RelocBlock { virtual_address: u32, count: u32, } impl RelocBlock { fn size(&self) -> u32 { mem::size_of::() as u32 + self.count * mem::size_of::() as u32 } } object-0.36.5/src/write/string.rs000064400000000000000000000126341046102023000147700ustar 00000000000000use alloc::vec::Vec; #[cfg(feature = "write_std")] type IndexSet = indexmap::IndexSet; #[cfg(not(feature = "write_std"))] type IndexSet = indexmap::IndexSet; /// An identifier for an entry in a string table. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct StringId(usize); #[derive(Debug, Default)] pub(crate) struct StringTable<'a> { strings: IndexSet<&'a [u8]>, offsets: Vec, } impl<'a> StringTable<'a> { /// Add a string to the string table. /// /// Panics if the string table has already been written, or /// if the string contains a null byte. pub fn add(&mut self, string: &'a [u8]) -> StringId { assert!(self.offsets.is_empty()); assert!(!string.contains(&0)); let id = self.strings.insert_full(string).0; StringId(id) } /// Return the id of the given string. /// /// Panics if the string is not in the string table. #[allow(dead_code)] pub fn get_id(&self, string: &[u8]) -> StringId { let id = self.strings.get_index_of(string).unwrap(); StringId(id) } /// Return the string for the given id. /// /// Panics if the string is not in the string table. #[allow(dead_code)] pub fn get_string(&self, id: StringId) -> &'a [u8] { self.strings.get_index(id.0).unwrap() } /// Return the offset of the given string. /// /// Panics if the string table has not been written, or /// if the string is not in the string table. pub fn get_offset(&self, id: StringId) -> usize { self.offsets[id.0] } /// Append the string table to the given `Vec`, and /// calculate the list of string offsets. /// /// `base` is the initial string table offset. For example, /// this should be 1 for ELF, to account for the initial /// null byte (which must have been written by the caller). /// /// Panics if the string table has already been written. pub fn write(&mut self, base: usize, w: &mut Vec) { assert!(self.offsets.is_empty()); let mut ids: Vec<_> = (0..self.strings.len()).collect(); sort(&mut ids, 1, &self.strings); self.offsets = vec![0; ids.len()]; let mut offset = base; let mut previous = &[][..]; for id in ids { let string = self.strings.get_index(id).unwrap(); if previous.ends_with(string) { self.offsets[id] = offset - string.len() - 1; } else { self.offsets[id] = offset; w.extend_from_slice(string); w.push(0); offset += string.len() + 1; previous = string; } } } /// Calculate the size in bytes of the string table. /// /// `base` is the initial string table offset. For example, /// this should be 1 for ELF, to account for the initial /// null byte. #[allow(dead_code)] pub fn size(&self, base: usize) -> usize { // TODO: cache this result? let mut ids: Vec<_> = (0..self.strings.len()).collect(); sort(&mut ids, 1, &self.strings); let mut size = base; let mut previous = &[][..]; for id in ids { let string = self.strings.get_index(id).unwrap(); if !previous.ends_with(string) { size += string.len() + 1; previous = string; } } size } } // Multi-key quicksort. // // Ordering is such that if a string is a suffix of at least one other string, // then it is placed immediately after one of those strings. That is: // - comparison starts at the end of the string // - shorter strings come later // // Based on the implementation in LLVM. fn sort(mut ids: &mut [usize], mut pos: usize, strings: &IndexSet<&[u8]>) { loop { if ids.len() <= 1 { return; } let pivot = byte(ids[0], pos, strings); let mut lower = 0; let mut upper = ids.len(); let mut i = 1; while i < upper { let b = byte(ids[i], pos, strings); if b > pivot { ids.swap(lower, i); lower += 1; i += 1; } else if b < pivot { upper -= 1; ids.swap(upper, i); } else { i += 1; } } sort(&mut ids[..lower], pos, strings); sort(&mut ids[upper..], pos, strings); if pivot == 0 { return; } ids = &mut ids[lower..upper]; pos += 1; } } fn byte(id: usize, pos: usize, strings: &IndexSet<&[u8]>) -> u8 { let string = strings.get_index(id).unwrap(); let len = string.len(); if len >= pos { string[len - pos] } else { // We know the strings don't contain null bytes. 0 } } #[cfg(test)] mod tests { use super::*; #[test] fn string_table() { let mut table = StringTable::default(); let id0 = table.add(b""); let id1 = table.add(b"foo"); let id2 = table.add(b"bar"); let id3 = table.add(b"foobar"); let mut data = Vec::new(); data.push(0); table.write(1, &mut data); assert_eq!(data, b"\0foobar\0foo\0"); assert_eq!(table.get_offset(id0), 11); assert_eq!(table.get_offset(id1), 8); assert_eq!(table.get_offset(id2), 4); assert_eq!(table.get_offset(id3), 1); } } object-0.36.5/src/write/util.rs000064400000000000000000000155171046102023000144420ustar 00000000000000use alloc::vec::Vec; #[cfg(feature = "std")] use std::{io, mem}; use crate::pod::{bytes_of, bytes_of_slice, Pod}; /// Trait for writable buffer. #[allow(clippy::len_without_is_empty)] pub trait WritableBuffer { /// Returns position/offset for data to be written at. /// /// Should only be used in debug assertions fn len(&self) -> usize; /// Reserves specified number of bytes in the buffer. /// /// This will be called exactly once before writing anything to the buffer, /// and the given size is the exact total number of bytes that will be written. fn reserve(&mut self, size: usize) -> Result<(), ()>; /// Writes zero bytes at the end of the buffer until the buffer /// has the specified length. fn resize(&mut self, new_len: usize); /// Writes the specified slice of bytes at the end of the buffer. fn write_bytes(&mut self, val: &[u8]); /// Writes the specified `Pod` type at the end of the buffer. fn write_pod(&mut self, val: &T) where Self: Sized, { self.write_bytes(bytes_of(val)) } /// Writes the specified `Pod` slice at the end of the buffer. fn write_pod_slice(&mut self, val: &[T]) where Self: Sized, { self.write_bytes(bytes_of_slice(val)) } } impl<'a> dyn WritableBuffer + 'a { /// Writes the specified `Pod` type at the end of the buffer. pub fn write(&mut self, val: &T) { self.write_bytes(bytes_of(val)) } /// Writes the specified `Pod` slice at the end of the buffer. pub fn write_slice(&mut self, val: &[T]) { self.write_bytes(bytes_of_slice(val)) } } impl WritableBuffer for Vec { #[inline] fn len(&self) -> usize { self.len() } #[inline] fn reserve(&mut self, size: usize) -> Result<(), ()> { debug_assert!(self.is_empty()); self.reserve(size); Ok(()) } #[inline] fn resize(&mut self, new_len: usize) { debug_assert!(new_len >= self.len()); self.resize(new_len, 0); } #[inline] fn write_bytes(&mut self, val: &[u8]) { debug_assert!(self.len() + val.len() <= self.capacity()); self.extend_from_slice(val) } } /// A [`WritableBuffer`] that streams data to a [`Write`](std::io::Write) implementation. /// /// [`Self::result`] must be called to determine if an I/O error occurred during writing. /// /// It is advisable to use a buffered writer like [`BufWriter`](std::io::BufWriter) /// instead of an unbuffered writer like [`File`](std::fs::File). #[cfg(feature = "std")] #[derive(Debug)] pub struct StreamingBuffer { writer: W, len: usize, result: Result<(), io::Error>, } #[cfg(feature = "std")] impl StreamingBuffer { /// Create a new `StreamingBuffer` backed by the given writer. pub fn new(writer: W) -> Self { StreamingBuffer { writer, len: 0, result: Ok(()), } } /// Unwraps this [`StreamingBuffer`] giving back the original writer. pub fn into_inner(self) -> W { self.writer } /// Returns any error that occurred during writing. pub fn result(&mut self) -> Result<(), io::Error> { mem::replace(&mut self.result, Ok(())) } } #[cfg(feature = "std")] impl WritableBuffer for StreamingBuffer { #[inline] fn len(&self) -> usize { self.len } #[inline] fn reserve(&mut self, _size: usize) -> Result<(), ()> { Ok(()) } #[inline] fn resize(&mut self, new_len: usize) { debug_assert!(self.len <= new_len); while self.len < new_len { let write_amt = (new_len - self.len - 1) % 1024 + 1; self.write_bytes(&[0; 1024][..write_amt]); } } #[inline] fn write_bytes(&mut self, val: &[u8]) { if self.result.is_ok() { self.result = self.writer.write_all(val); } self.len += val.len(); } } /// A trait for mutable byte slices. /// /// It provides convenience methods for `Pod` types. pub(crate) trait BytesMut { fn write_at(self, offset: usize, val: &T) -> Result<(), ()>; } impl<'a> BytesMut for &'a mut [u8] { #[inline] fn write_at(self, offset: usize, val: &T) -> Result<(), ()> { let src = bytes_of(val); let dest = self.get_mut(offset..).ok_or(())?; let dest = dest.get_mut(..src.len()).ok_or(())?; dest.copy_from_slice(src); Ok(()) } } /// Write an unsigned number using the LEB128 encoding to a buffer. /// /// Returns the number of bytes written. #[allow(dead_code)] pub(crate) fn write_uleb128(buf: &mut Vec, mut val: u64) -> usize { let mut len = 0; loop { let mut byte = (val & 0x7f) as u8; val >>= 7; let done = val == 0; if !done { byte |= 0x80; } buf.push(byte); len += 1; if done { return len; } } } /// Write a signed number using the LEB128 encoding to a buffer. /// /// Returns the number of bytes written. #[allow(dead_code)] pub(crate) fn write_sleb128(buf: &mut Vec, mut val: i64) -> usize { let mut len = 0; loop { let mut byte = val as u8; // Keep the sign bit for testing val >>= 6; let done = val == 0 || val == -1; if done { byte &= !0x80; } else { // Remove the sign bit val >>= 1; byte |= 0x80; } buf.push(byte); len += 1; if done { return len; } } } pub(crate) fn align(offset: usize, size: usize) -> usize { (offset + (size - 1)) & !(size - 1) } #[allow(dead_code)] pub(crate) fn align_u32(offset: u32, size: u32) -> u32 { (offset + (size - 1)) & !(size - 1) } #[allow(dead_code)] pub(crate) fn align_u64(offset: u64, size: u64) -> u64 { (offset + (size - 1)) & !(size - 1) } pub(crate) fn write_align(buffer: &mut dyn WritableBuffer, size: usize) { let new_len = align(buffer.len(), size); buffer.resize(new_len); } #[cfg(test)] mod tests { use super::*; #[test] fn bytes_mut() { let data = vec![0x01, 0x23, 0x45, 0x67]; let mut bytes = data.clone(); bytes.extend_from_slice(bytes_of(&u16::to_be(0x89ab))); assert_eq!(bytes, [0x01, 0x23, 0x45, 0x67, 0x89, 0xab]); let mut bytes = data.clone(); assert_eq!(bytes.write_at(0, &u16::to_be(0x89ab)), Ok(())); assert_eq!(bytes, [0x89, 0xab, 0x45, 0x67]); let mut bytes = data.clone(); assert_eq!(bytes.write_at(2, &u16::to_be(0x89ab)), Ok(())); assert_eq!(bytes, [0x01, 0x23, 0x89, 0xab]); assert_eq!(bytes.write_at(3, &u16::to_be(0x89ab)), Err(())); assert_eq!(bytes.write_at(4, &u16::to_be(0x89ab)), Err(())); assert_eq!([].write_at(0, &u32::to_be(0x89ab)), Err(())); } } object-0.36.5/src/write/xcoff.rs000064400000000000000000000572011046102023000145660ustar 00000000000000use core::mem; use crate::endian::{BigEndian as BE, I16, U16, U32}; use crate::write::string::*; use crate::write::util::*; use crate::write::*; use crate::xcoff; #[derive(Default, Clone, Copy)] struct SectionOffsets { address: u64, data_offset: usize, reloc_offset: usize, } #[derive(Default, Clone, Copy)] struct SymbolOffsets { index: usize, str_id: Option, aux_count: u8, storage_class: u8, } impl<'a> Object<'a> { pub(crate) fn xcoff_section_info( &self, section: StandardSection, ) -> (&'static [u8], &'static [u8], SectionKind, SectionFlags) { match section { StandardSection::Text => (&[], &b".text"[..], SectionKind::Text, SectionFlags::None), StandardSection::Data => (&[], &b".data"[..], SectionKind::Data, SectionFlags::None), StandardSection::ReadOnlyData | StandardSection::ReadOnlyDataWithRel | StandardSection::ReadOnlyString => ( &[], &b".rdata"[..], SectionKind::ReadOnlyData, SectionFlags::None, ), StandardSection::UninitializedData => ( &[], &b".bss"[..], SectionKind::UninitializedData, SectionFlags::None, ), StandardSection::Tls => (&[], &b".tdata"[..], SectionKind::Tls, SectionFlags::None), StandardSection::UninitializedTls => ( &[], &b".tbss"[..], SectionKind::UninitializedTls, SectionFlags::None, ), StandardSection::TlsVariables => { // Unsupported section. (&[], &[], SectionKind::TlsVariables, SectionFlags::None) } StandardSection::Common => { // Unsupported section. (&[], &[], SectionKind::Common, SectionFlags::None) } StandardSection::GnuProperty => { // Unsupported section. (&[], &[], SectionKind::Note, SectionFlags::None) } } } pub(crate) fn xcoff_translate_relocation(&mut self, reloc: &mut Relocation) -> Result<()> { let (kind, _encoding, size) = if let RelocationFlags::Generic { kind, encoding, size, } = reloc.flags { (kind, encoding, size) } else { return Ok(()); }; let r_rtype = match kind { RelocationKind::Absolute => xcoff::R_POS, RelocationKind::Relative => xcoff::R_REL, RelocationKind::Got => xcoff::R_TOC, _ => { return Err(Error(format!("unimplemented relocation {:?}", reloc))); } }; let r_rsize = size - 1; reloc.flags = RelocationFlags::Xcoff { r_rtype, r_rsize }; Ok(()) } pub(crate) fn xcoff_adjust_addend(&mut self, relocation: &mut Relocation) -> Result { let r_rtype = if let RelocationFlags::Xcoff { r_rtype, .. } = relocation.flags { r_rtype } else { return Err(Error(format!("invalid relocation flags {:?}", relocation))); }; if r_rtype == xcoff::R_REL { relocation.addend += 4; } Ok(true) } pub(crate) fn xcoff_relocation_size(&self, reloc: &Relocation) -> Result { let r_rsize = if let RelocationFlags::Xcoff { r_rsize, .. } = reloc.flags { r_rsize } else { return Err(Error(format!("unexpected relocation {:?}", reloc))); }; Ok(r_rsize + 1) } pub(crate) fn xcoff_write(&self, buffer: &mut dyn WritableBuffer) -> Result<()> { let is_64 = match self.architecture.address_size().unwrap() { AddressSize::U8 | AddressSize::U16 | AddressSize::U32 => false, AddressSize::U64 => true, }; let (hdr_size, sechdr_size, rel_size, sym_size) = if is_64 { ( mem::size_of::(), mem::size_of::(), mem::size_of::(), mem::size_of::(), ) } else { ( mem::size_of::(), mem::size_of::(), mem::size_of::(), mem::size_of::(), ) }; // Calculate offsets and build strtab. let mut offset = 0; let mut strtab = StringTable::default(); // We place the shared address 0 immediately after the section header table. let mut address = 0; // XCOFF file header. offset += hdr_size; // Section headers. offset += self.sections.len() * sechdr_size; // Calculate size of section data. let mut section_offsets = vec![SectionOffsets::default(); self.sections.len()]; for (index, section) in self.sections.iter().enumerate() { let len = section.data.len(); let sectype = section.kind; // Section address should be 0 for all sections except the .text, .data, and .bss sections. if sectype == SectionKind::Data || sectype == SectionKind::Text || sectype == SectionKind::UninitializedData { section_offsets[index].address = address as u64; address += len; address = align(address, 4); } else { section_offsets[index].address = 0; } if len != 0 { // Set the default section alignment as 4. offset = align(offset, 4); section_offsets[index].data_offset = offset; offset += len; } else { section_offsets[index].data_offset = 0; } } // Calculate size of relocations. for (index, section) in self.sections.iter().enumerate() { let count = section.relocations.len(); if count != 0 { section_offsets[index].reloc_offset = offset; offset += count * rel_size; } else { section_offsets[index].reloc_offset = 0; } } // Calculate size of symbols. let mut file_str_id = None; let mut symbol_offsets = vec![SymbolOffsets::default(); self.symbols.len()]; let mut symtab_count = 0; for (index, symbol) in self.symbols.iter().enumerate() { symbol_offsets[index].index = symtab_count; symtab_count += 1; let storage_class = if let SymbolFlags::Xcoff { n_sclass, .. } = symbol.flags { n_sclass } else { match symbol.kind { SymbolKind::File => xcoff::C_FILE, SymbolKind::Text | SymbolKind::Data | SymbolKind::Tls => { if symbol.is_local() { xcoff::C_STAT } else if symbol.weak { xcoff::C_WEAKEXT } else { xcoff::C_EXT } } SymbolKind::Section | SymbolKind::Label | SymbolKind::Unknown => { return Err(Error(format!( "unimplemented symbol `{}` kind {:?}", symbol.name().unwrap_or(""), symbol.kind ))); } } }; symbol_offsets[index].storage_class = storage_class; if storage_class == xcoff::C_FILE { if is_64 && file_str_id.is_none() { file_str_id = Some(strtab.add(b".file")); } if symbol.name.len() > 8 { symbol_offsets[index].str_id = Some(strtab.add(&symbol.name)); } } else if is_64 || symbol.name.len() > 8 { symbol_offsets[index].str_id = Some(strtab.add(&symbol.name)); } symbol_offsets[index].aux_count = 0; match storage_class { xcoff::C_FILE => { symbol_offsets[index].aux_count = 1; symtab_count += 1; } xcoff::C_EXT | xcoff::C_WEAKEXT | xcoff::C_HIDEXT => { symbol_offsets[index].aux_count = 1; symtab_count += 1; } // TODO: support auxiliary entry for other types of symbol. _ => {} } } let symtab_offset = offset; let symtab_len = symtab_count * sym_size; offset += symtab_len; // Calculate size of strtab. let strtab_offset = offset; let mut strtab_data = Vec::new(); // First 4 bytes of strtab are the length. strtab.write(4, &mut strtab_data); let strtab_len = strtab_data.len() + 4; offset += strtab_len; // Start writing. buffer .reserve(offset) .map_err(|_| Error(String::from("Cannot allocate buffer")))?; // Write file header. if is_64 { let header = xcoff::FileHeader64 { f_magic: U16::new(BE, xcoff::MAGIC_64), f_nscns: U16::new(BE, self.sections.len() as u16), f_timdat: U32::new(BE, 0), f_symptr: U64::new(BE, symtab_offset as u64), f_nsyms: U32::new(BE, symtab_count as u32), f_opthdr: U16::new(BE, 0), f_flags: match self.flags { FileFlags::Xcoff { f_flags } => U16::new(BE, f_flags), _ => U16::default(), }, }; buffer.write(&header); } else { let header = xcoff::FileHeader32 { f_magic: U16::new(BE, xcoff::MAGIC_32), f_nscns: U16::new(BE, self.sections.len() as u16), f_timdat: U32::new(BE, 0), f_symptr: U32::new(BE, symtab_offset as u32), f_nsyms: U32::new(BE, symtab_count as u32), f_opthdr: U16::new(BE, 0), f_flags: match self.flags { FileFlags::Xcoff { f_flags } => U16::new(BE, f_flags), _ => U16::default(), }, }; buffer.write(&header); } // Write section headers. for (index, section) in self.sections.iter().enumerate() { let mut sectname = [0; 8]; sectname .get_mut(..section.name.len()) .ok_or_else(|| { Error(format!( "section name `{}` is too long", section.name().unwrap_or(""), )) })? .copy_from_slice(§ion.name); let flags = if let SectionFlags::Xcoff { s_flags } = section.flags { s_flags } else { match section.kind { SectionKind::Text | SectionKind::ReadOnlyData | SectionKind::ReadOnlyString | SectionKind::ReadOnlyDataWithRel => xcoff::STYP_TEXT, SectionKind::Data => xcoff::STYP_DATA, SectionKind::UninitializedData => xcoff::STYP_BSS, SectionKind::Tls => xcoff::STYP_TDATA, SectionKind::UninitializedTls => xcoff::STYP_TBSS, SectionKind::OtherString => xcoff::STYP_INFO, SectionKind::Debug | SectionKind::DebugString => xcoff::STYP_DEBUG, SectionKind::Other | SectionKind::Metadata => 0, SectionKind::Note | SectionKind::Linker | SectionKind::Common | SectionKind::Unknown | SectionKind::TlsVariables | SectionKind::Elf(_) => { return Err(Error(format!( "unimplemented section `{}` kind {:?}", section.name().unwrap_or(""), section.kind ))); } } .into() }; if is_64 { let section_header = xcoff::SectionHeader64 { s_name: sectname, s_paddr: U64::new(BE, section_offsets[index].address), // This field has the same value as the s_paddr field. s_vaddr: U64::new(BE, section_offsets[index].address), s_size: U64::new(BE, section.data.len() as u64), s_scnptr: U64::new(BE, section_offsets[index].data_offset as u64), s_relptr: U64::new(BE, section_offsets[index].reloc_offset as u64), s_lnnoptr: U64::new(BE, 0), s_nreloc: U32::new(BE, section.relocations.len() as u32), s_nlnno: U32::new(BE, 0), s_flags: U32::new(BE, flags), s_reserve: U32::new(BE, 0), }; buffer.write(§ion_header); } else { let section_header = xcoff::SectionHeader32 { s_name: sectname, s_paddr: U32::new(BE, section_offsets[index].address as u32), // This field has the same value as the s_paddr field. s_vaddr: U32::new(BE, section_offsets[index].address as u32), s_size: U32::new(BE, section.data.len() as u32), s_scnptr: U32::new(BE, section_offsets[index].data_offset as u32), s_relptr: U32::new(BE, section_offsets[index].reloc_offset as u32), s_lnnoptr: U32::new(BE, 0), // TODO: If more than 65,534 relocation entries are required, the field // value will be 65535, and an STYP_OVRFLO section header will contain // the actual count of relocation entries in the s_paddr field. s_nreloc: U16::new(BE, section.relocations.len() as u16), s_nlnno: U16::new(BE, 0), s_flags: U32::new(BE, flags), }; buffer.write(§ion_header); } } // Write section data. for (index, section) in self.sections.iter().enumerate() { let len = section.data.len(); if len != 0 { write_align(buffer, 4); debug_assert_eq!(section_offsets[index].data_offset, buffer.len()); buffer.write_bytes(§ion.data); } } // Write relocations. for (index, section) in self.sections.iter().enumerate() { if !section.relocations.is_empty() { debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len()); for reloc in §ion.relocations { let (r_rtype, r_rsize) = if let RelocationFlags::Xcoff { r_rtype, r_rsize } = reloc.flags { (r_rtype, r_rsize) } else { return Err(Error("invalid relocation flags".into())); }; if is_64 { let xcoff_rel = xcoff::Rel64 { r_vaddr: U64::new(BE, reloc.offset), r_symndx: U32::new(BE, symbol_offsets[reloc.symbol.0].index as u32), r_rsize, r_rtype, }; buffer.write(&xcoff_rel); } else { let xcoff_rel = xcoff::Rel32 { r_vaddr: U32::new(BE, reloc.offset as u32), r_symndx: U32::new(BE, symbol_offsets[reloc.symbol.0].index as u32), r_rsize, r_rtype, }; buffer.write(&xcoff_rel); } } } } // Write symbols. debug_assert_eq!(symtab_offset, buffer.len()); for (index, symbol) in self.symbols.iter().enumerate() { let (n_value, section_kind) = if let SymbolSection::Section(id) = symbol.section { ( section_offsets[id.0].address + symbol.value, self.sections[id.0].kind, ) } else { (symbol.value, SectionKind::Unknown) }; let n_scnum = match symbol.section { SymbolSection::None => { debug_assert_eq!(symbol.kind, SymbolKind::File); xcoff::N_DEBUG } SymbolSection::Undefined | SymbolSection::Common => xcoff::N_UNDEF, SymbolSection::Absolute => xcoff::N_ABS, SymbolSection::Section(id) => id.0 as i16 + 1, }; let n_sclass = symbol_offsets[index].storage_class; let n_type = if (symbol.scope == SymbolScope::Linkage) && (n_sclass == xcoff::C_EXT || n_sclass == xcoff::C_WEAKEXT || n_sclass == xcoff::C_HIDEXT) { xcoff::SYM_V_HIDDEN } else { 0 }; let n_numaux = symbol_offsets[index].aux_count; if is_64 { let str_id = if n_sclass == xcoff::C_FILE { file_str_id.unwrap() } else { symbol_offsets[index].str_id.unwrap() }; let xcoff_sym = xcoff::Symbol64 { n_value: U64::new(BE, n_value), n_offset: U32::new(BE, strtab.get_offset(str_id) as u32), n_scnum: I16::new(BE, n_scnum), n_type: U16::new(BE, n_type), n_sclass, n_numaux, }; buffer.write(&xcoff_sym); } else { let mut sym_name = [0; 8]; if n_sclass == xcoff::C_FILE { sym_name[..5].copy_from_slice(b".file"); } else if symbol.name.len() <= 8 { sym_name[..symbol.name.len()].copy_from_slice(&symbol.name[..]); } else { let str_offset = strtab.get_offset(symbol_offsets[index].str_id.unwrap()); sym_name[4..8].copy_from_slice(&u32::to_be_bytes(str_offset as u32)); } let xcoff_sym = xcoff::Symbol32 { n_name: sym_name, n_value: U32::new(BE, n_value as u32), n_scnum: I16::new(BE, n_scnum), n_type: U16::new(BE, n_type), n_sclass, n_numaux, }; buffer.write(&xcoff_sym); } // Generate auxiliary entries. if n_sclass == xcoff::C_FILE { debug_assert_eq!(n_numaux, 1); let mut x_fname = [0; 8]; if symbol.name.len() <= 8 { x_fname[..symbol.name.len()].copy_from_slice(&symbol.name[..]); } else { let str_offset = strtab.get_offset(symbol_offsets[index].str_id.unwrap()); x_fname[4..8].copy_from_slice(&u32::to_be_bytes(str_offset as u32)); } if is_64 { let file_aux = xcoff::FileAux64 { x_fname, x_fpad: Default::default(), x_ftype: xcoff::XFT_FN, x_freserve: Default::default(), x_auxtype: xcoff::AUX_FILE, }; buffer.write(&file_aux); } else { let file_aux = xcoff::FileAux32 { x_fname, x_fpad: Default::default(), x_ftype: xcoff::XFT_FN, x_freserve: Default::default(), }; buffer.write(&file_aux); } } else if n_sclass == xcoff::C_EXT || n_sclass == xcoff::C_WEAKEXT || n_sclass == xcoff::C_HIDEXT { debug_assert_eq!(n_numaux, 1); let (x_smtyp, x_smclas) = if let SymbolFlags::Xcoff { x_smtyp, x_smclas, .. } = symbol.flags { (x_smtyp, x_smclas) } else { match symbol.kind { SymbolKind::Text => (xcoff::XTY_SD, xcoff::XMC_PR), SymbolKind::Data => { if section_kind == SectionKind::UninitializedData { (xcoff::XTY_CM, xcoff::XMC_BS) } else if section_kind == SectionKind::ReadOnlyData { (xcoff::XTY_SD, xcoff::XMC_RO) } else { (xcoff::XTY_SD, xcoff::XMC_RW) } } SymbolKind::Tls => { if section_kind == SectionKind::UninitializedTls { (xcoff::XTY_CM, xcoff::XMC_UL) } else { (xcoff::XTY_SD, xcoff::XMC_TL) } } _ => { return Err(Error(format!( "unimplemented symbol `{}` kind {:?}", symbol.name().unwrap_or(""), symbol.kind ))); } } }; let scnlen = if let SymbolFlags::Xcoff { containing_csect: Some(containing_csect), .. } = symbol.flags { symbol_offsets[containing_csect.0].index as u64 } else { symbol.size }; if is_64 { let csect_aux = xcoff::CsectAux64 { x_scnlen_lo: U32::new(BE, (scnlen & 0xFFFFFFFF) as u32), x_scnlen_hi: U32::new(BE, ((scnlen >> 32) & 0xFFFFFFFF) as u32), x_parmhash: U32::new(BE, 0), x_snhash: U16::new(BE, 0), x_smtyp, x_smclas, pad: 0, x_auxtype: xcoff::AUX_CSECT, }; buffer.write(&csect_aux); } else { let csect_aux = xcoff::CsectAux32 { x_scnlen: U32::new(BE, scnlen as u32), x_parmhash: U32::new(BE, 0), x_snhash: U16::new(BE, 0), x_smtyp, x_smclas, x_stab: U32::new(BE, 0), x_snstab: U16::new(BE, 0), }; buffer.write(&csect_aux); } } } // Write string table. debug_assert_eq!(strtab_offset, buffer.len()); buffer.write_bytes(&u32::to_be_bytes(strtab_len as u32)); buffer.write_bytes(&strtab_data); debug_assert_eq!(offset, buffer.len()); Ok(()) } } object-0.36.5/src/xcoff.rs000064400000000000000000000660461046102023000134430ustar 00000000000000//! XCOFF definitions //! //! These definitions are independent of read/write support, although we do implement //! some traits useful for those. //! //! This module is the equivalent of /usr/include/xcoff.h, and is based heavily on it. #![allow(missing_docs)] use crate::endian::{BigEndian as BE, I16, U16, U32, U64}; use crate::pod::Pod; /// The header at the start of every 32-bit XCOFF file. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct FileHeader32 { /// Magic number. Must be 0x01DF. pub f_magic: U16, /// Number of sections. pub f_nscns: U16, /// Time and date of file creation. pub f_timdat: U32, /// Byte offset to symbol table start. pub f_symptr: U32, /// Number of entries in symbol table. pub f_nsyms: U32, /// Number of bytes in optional header pub f_opthdr: U16, /// Extra flags. pub f_flags: U16, } /// The header at the start of every 64-bit XCOFF file. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct FileHeader64 { /// Magic number. Must be 0x01F7. pub f_magic: U16, /// Number of sections. pub f_nscns: U16, /// Time and date of file creation pub f_timdat: U32, /// Byte offset to symbol table start. pub f_symptr: U64, /// Number of bytes in optional header pub f_opthdr: U16, /// Extra flags. pub f_flags: U16, /// Number of entries in symbol table. pub f_nsyms: U32, } // Values for `f_magic`. // /// the 64-bit mach magic number pub const MAGIC_64: u16 = 0x01F7; /// the 32-bit mach magic number pub const MAGIC_32: u16 = 0x01DF; // Values for `f_flags`. // /// Indicates that the relocation information for binding has been removed from /// the file. pub const F_RELFLG: u16 = 0x0001; /// Indicates that the file is executable. No unresolved external references exist. pub const F_EXEC: u16 = 0x0002; /// Indicates that line numbers have been stripped from the file by a utility program. pub const F_LNNO: u16 = 0x0004; /// Indicates that the file was profiled with the fdpr command. pub const F_FDPR_PROF: u16 = 0x0010; /// Indicates that the file was reordered with the fdpr command. pub const F_FDPR_OPTI: u16 = 0x0020; /// Indicates that the file uses Very Large Program Support. pub const F_DSA: u16 = 0x0040; /// Indicates that one of the members of the auxiliary header specifying the /// medium page sizes is non-zero. pub const F_VARPG: u16 = 0x0100; /// Indicates the file is dynamically loadable and executable. External references /// are resolved by way of imports, and the file might contain exports and loader /// relocation. pub const F_DYNLOAD: u16 = 0x1000; /// Indicates the file is a shared object (shared library). The file is separately /// loadable. That is, it is not normally bound with other objects, and its loader /// exports symbols are used as automatic import symbols for other object files. pub const F_SHROBJ: u16 = 0x2000; /// If the object file is a member of an archive, it can be loaded by the system /// loader, but the member is ignored by the binder. If the object file is not in /// an archive, this flag has no effect. pub const F_LOADONLY: u16 = 0x4000; /// The auxiliary header immediately following file header. If the value of the /// f_opthdr field in the file header is 0, the auxiliary header does not exist. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct AuxHeader32 { /// Flags. pub o_mflag: U16, /// Version. pub o_vstamp: U16, /// Text size in bytes. pub o_tsize: U32, /// Initialized data size in bytes. pub o_dsize: U32, /// Uninitialized data size in bytes. pub o_bsize: U32, /// Entry point descriptor (virtual address). pub o_entry: U32, /// Base address of text (virtual address). pub o_text_start: U32, /// Base address of data (virtual address). pub o_data_start: U32, /// Address of TOC anchor. pub o_toc: U32, /// Section number for entry point. pub o_snentry: U16, /// Section number for .text. pub o_sntext: U16, /// Section number for .data. pub o_sndata: U16, /// Section number for TOC. pub o_sntoc: U16, /// Section number for loader data. pub o_snloader: U16, /// Section number for .bss. pub o_snbss: U16, /// Maximum alignment for .text. pub o_algntext: U16, /// Maximum alignment for .data. pub o_algndata: U16, /// Module type field. pub o_modtype: U16, /// Bit flags - cpu types of objects. pub o_cpuflag: u8, /// Reserved for CPU type. pub o_cputype: u8, /// Maximum stack size allowed (bytes). pub o_maxstack: U32, /// Maximum data size allowed (bytes). pub o_maxdata: U32, /// Reserved for debuggers. pub o_debugger: U32, /// Requested text page size. pub o_textpsize: u8, /// Requested data page size. pub o_datapsize: u8, /// Requested stack page size. pub o_stackpsize: u8, /// Flags and thread-local storage alignment. pub o_flags: u8, /// Section number for .tdata. pub o_sntdata: U16, /// Section number for .tbss. pub o_sntbss: U16, } /// The auxiliary header immediately following file header. If the value of the /// f_opthdr field in the file header is 0, the auxiliary header does not exist. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct AuxHeader64 { /// Flags. pub o_mflag: U16, /// Version. pub o_vstamp: U16, /// Reserved for debuggers. pub o_debugger: U32, /// Base address of text (virtual address). pub o_text_start: U64, /// Base address of data (virtual address). pub o_data_start: U64, /// Address of TOC anchor. pub o_toc: U64, /// Section number for entry point. pub o_snentry: U16, /// Section number for .text. pub o_sntext: U16, /// Section number for .data. pub o_sndata: U16, /// Section number for TOC. pub o_sntoc: U16, /// Section number for loader data. pub o_snloader: U16, /// Section number for .bss. pub o_snbss: U16, /// Maximum alignment for .text. pub o_algntext: U16, /// Maximum alignment for .data. pub o_algndata: U16, /// Module type field. pub o_modtype: U16, /// Bit flags - cpu types of objects. pub o_cpuflag: u8, /// Reserved for CPU type. pub o_cputype: u8, /// Requested text page size. pub o_textpsize: u8, /// Requested data page size. pub o_datapsize: u8, /// Requested stack page size. pub o_stackpsize: u8, /// Flags and thread-local storage alignment. pub o_flags: u8, /// Text size in bytes. pub o_tsize: U64, /// Initialized data size in bytes. pub o_dsize: U64, /// Uninitialized data size in bytes. pub o_bsize: U64, /// Entry point descriptor (virtual address). pub o_entry: U64, /// Maximum stack size allowed (bytes). pub o_maxstack: U64, /// Maximum data size allowed (bytes). pub o_maxdata: U64, /// Section number for .tdata. pub o_sntdata: U16, /// Section number for .tbss. pub o_sntbss: U16, /// XCOFF64 flags. pub o_x64flags: U16, /// Reserved. pub o_resv3a: U16, /// Reserved. pub o_resv3: [U32; 2], } /// Some AIX programs generate auxiliary headers for 32-bit object files that /// end after the data_start field. pub const AOUTHSZ_SHORT: u16 = 28; /// Section header. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct SectionHeader32 { /// Section name. pub s_name: [u8; 8], /// Physical address. pub s_paddr: U32, /// Virtual address (same as physical address). pub s_vaddr: U32, /// Section size. pub s_size: U32, /// Offset in file to raw data for section. pub s_scnptr: U32, /// Offset in file to relocation entries for section. pub s_relptr: U32, /// Offset in file to line number entries for section. pub s_lnnoptr: U32, /// Number of relocation entries. pub s_nreloc: U16, /// Number of line number entries. pub s_nlnno: U16, /// Flags to define the section type. pub s_flags: U32, } /// Section header. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct SectionHeader64 { /// Section name. pub s_name: [u8; 8], /// Physical address. pub s_paddr: U64, /// Virtual address (same as physical address). pub s_vaddr: U64, /// Section size. pub s_size: U64, /// Offset in file to raw data for section. pub s_scnptr: U64, /// Offset in file to relocation entries for section. pub s_relptr: U64, /// Offset in file to line number entries for section. pub s_lnnoptr: U64, /// Number of relocation entries. pub s_nreloc: U32, /// Number of line number entries. pub s_nlnno: U32, /// Flags to define the section type. pub s_flags: U32, /// Reserved. pub s_reserve: U32, } // Values for `s_flags`. // /// "regular" section pub const STYP_REG: u16 = 0x00; /// Specifies a pad section. A section of this type is used to provide alignment /// padding between sections within an XCOFF executable object file. This section /// header type is obsolete since padding is allowed in an XCOFF file without a /// corresponding pad section header. pub const STYP_PAD: u16 = 0x08; /// Specifies a DWARF debugging section, which provide source file and symbol /// information for the symbolic debugger. pub const STYP_DWARF: u16 = 0x10; /// Specifies an executable text (code) section. A section of this type contains /// the executable instructions of a program. pub const STYP_TEXT: u16 = 0x20; /// Specifies an initialized data section. A section of this type contains the /// initialized data and the TOC of a program. pub const STYP_DATA: u16 = 0x40; /// Specifies an uninitialized data section. A section header of this type /// defines the uninitialized data of a program. pub const STYP_BSS: u16 = 0x80; /// Specifies an exception section. A section of this type provides information /// to identify the reason that a trap or exception occurred within an executable /// object program. pub const STYP_EXCEPT: u16 = 0x0100; /// Specifies a comment section. A section of this type provides comments or data /// to special processing utility programs. pub const STYP_INFO: u16 = 0x0200; /// Specifies an initialized thread-local data section. pub const STYP_TDATA: u16 = 0x0400; /// Specifies an uninitialized thread-local data section. pub const STYP_TBSS: u16 = 0x0800; /// Specifies a loader section. A section of this type contains object file /// information for the system loader to load an XCOFF executable. The information /// includes imported symbols, exported symbols, relocation data, type-check /// information, and shared object names. pub const STYP_LOADER: u16 = 0x1000; /// Specifies a debug section. A section of this type contains stabstring /// information used by the symbolic debugger. pub const STYP_DEBUG: u16 = 0x2000; /// Specifies a type-check section. A section of this type contains /// parameter/argument type-check strings used by the binder. pub const STYP_TYPCHK: u16 = 0x4000; /// Specifies a relocation or line-number field overflow section. A section /// header of this type contains the count of relocation entries and line /// number entries for some other section. This section header is required /// when either of the counts exceeds 65,534. pub const STYP_OVRFLO: u16 = 0x8000; pub const SSUBTYP_DWINFO: u32 = 0x10000; pub const SSUBTYP_DWLINE: u32 = 0x20000; pub const SSUBTYP_DWPBNMS: u32 = 0x30000; pub const SSUBTYP_DWPBTYP: u32 = 0x40000; pub const SSUBTYP_DWARNGE: u32 = 0x50000; pub const SSUBTYP_DWABREV: u32 = 0x60000; pub const SSUBTYP_DWSTR: u32 = 0x70000; pub const SSUBTYP_DWRNGES: u32 = 0x80000; pub const SSUBTYP_DWLOC: u32 = 0x90000; pub const SSUBTYP_DWFRAME: u32 = 0xA0000; pub const SSUBTYP_DWMAC: u32 = 0xB0000; pub const SIZEOF_SYMBOL: usize = 18; #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct SymbolBytes(pub [u8; SIZEOF_SYMBOL]); /// Symbol table entry. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Symbol32 { /// Symbol name. /// /// If first 4 bytes are 0, then second 4 bytes are offset into string table. pub n_name: [u8; 8], /// Symbol value; storage class-dependent. pub n_value: U32, /// Section number of symbol. pub n_scnum: I16, /// Basic and derived type specification. pub n_type: U16, /// Storage class of symbol. pub n_sclass: u8, /// Number of auxiliary entries. pub n_numaux: u8, } /// Symbol table entry. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Symbol64 { /// Symbol value; storage class-dependent. pub n_value: U64, /// Offset of the name in string table or .debug section. pub n_offset: U32, /// Section number of symbol. pub n_scnum: I16, /// Basic and derived type specification. pub n_type: U16, /// Storage class of symbol. pub n_sclass: u8, /// Number of auxiliary entries. pub n_numaux: u8, } // Values for `n_scnum`. // /// A special symbolic debugging symbol. pub const N_DEBUG: i16 = -2; /// An absolute symbol. The symbol has a value but is not relocatable. pub const N_ABS: i16 = -1; /// An undefined external symbol. pub const N_UNDEF: i16 = 0; // Values for `n_type`. // /// Values for visibility as they would appear when encoded in the high 4 bits /// of the 16-bit unsigned n_type field of symbol table entries. Valid for /// 32-bit XCOFF only when the o_vstamp in the auxiliary header is greater than 1. pub const SYM_V_MASK: u16 = 0xF000; pub const SYM_V_INTERNAL: u16 = 0x1000; pub const SYM_V_HIDDEN: u16 = 0x2000; pub const SYM_V_PROTECTED: u16 = 0x3000; pub const SYM_V_EXPORTED: u16 = 0x4000; // Values for `n_sclass`. // // Storage classes used for symbolic debugging symbols. // /// Source file name and compiler information. pub const C_FILE: u8 = 103; /// Beginning of include file. pub const C_BINCL: u8 = 108; /// Ending of include file. pub const C_EINCL: u8 = 109; /// Global variable. pub const C_GSYM: u8 = 128; /// Statically allocated symbol. pub const C_STSYM: u8 = 133; /// Beginning of common block. pub const C_BCOMM: u8 = 135; /// End of common block. pub const C_ECOMM: u8 = 137; /// Alternate entry. pub const C_ENTRY: u8 = 141; /// Beginning of static block. pub const C_BSTAT: u8 = 143; /// End of static block. pub const C_ESTAT: u8 = 144; /// Global thread-local variable. pub const C_GTLS: u8 = 145; /// Static thread-local variable. pub const C_STTLS: u8 = 146; /// DWARF section symbol. pub const C_DWARF: u8 = 112; // // Storage classes used for absolute symbols. // /// Automatic variable allocated on stack. pub const C_LSYM: u8 = 129; /// Argument to subroutine allocated on stack. pub const C_PSYM: u8 = 130; /// Register variable. pub const C_RSYM: u8 = 131; /// Argument to function or procedure stored in register. pub const C_RPSYM: u8 = 132; /// Local member of common block. pub const C_ECOML: u8 = 136; /// Function or procedure. pub const C_FUN: u8 = 142; // // Storage classes used for undefined external symbols or symbols of general sections. // /// External symbol. pub const C_EXT: u8 = 2; /// Weak external symbol. pub const C_WEAKEXT: u8 = 111; // // Storage classes used for symbols of general sections. // /// Symbol table entry marked for deletion. pub const C_NULL: u8 = 0; /// Static. pub const C_STAT: u8 = 3; /// Beginning or end of inner block. pub const C_BLOCK: u8 = 100; /// Beginning or end of function. pub const C_FCN: u8 = 101; /// Un-named external symbol. pub const C_HIDEXT: u8 = 107; /// Comment string in .info section. pub const C_INFO: u8 = 110; /// Declaration of object (type). pub const C_DECL: u8 = 140; // // Storage classes - Obsolete/Undocumented. // /// Automatic variable. pub const C_AUTO: u8 = 1; /// Register variable. pub const C_REG: u8 = 4; /// External definition. pub const C_EXTDEF: u8 = 5; /// Label. pub const C_LABEL: u8 = 6; /// Undefined label. pub const C_ULABEL: u8 = 7; /// Member of structure. pub const C_MOS: u8 = 8; /// Function argument. pub const C_ARG: u8 = 9; /// Structure tag. pub const C_STRTAG: u8 = 10; /// Member of union. pub const C_MOU: u8 = 11; /// Union tag. pub const C_UNTAG: u8 = 12; /// Type definition. pub const C_TPDEF: u8 = 13; /// Undefined static. pub const C_USTATIC: u8 = 14; /// Enumeration tag. pub const C_ENTAG: u8 = 15; /// Member of enumeration. pub const C_MOE: u8 = 16; /// Register parameter. pub const C_REGPARM: u8 = 17; /// Bit field. pub const C_FIELD: u8 = 18; /// End of structure. pub const C_EOS: u8 = 102; /// Duplicate tag. pub const C_ALIAS: u8 = 105; /// Special storage class for external. pub const C_HIDDEN: u8 = 106; /// Physical end of function. pub const C_EFCN: u8 = 255; /// Reserved. pub const C_TCSYM: u8 = 134; /// File Auxiliary Entry for C_FILE Symbols. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct FileAux32 { /// The source file name or compiler-related string. /// /// If first 4 bytes are 0, then second 4 bytes are offset into string table. pub x_fname: [u8; 8], /// Pad size for file name. pub x_fpad: [u8; 6], /// The source-file string type. pub x_ftype: u8, /// Reserved. pub x_freserve: [u8; 3], } /// File Auxiliary Entry for C_FILE Symbols. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct FileAux64 { /// The source file name or compiler-related string. /// /// If first 4 bytes are 0, then second 4 bytes are offset into string table. pub x_fname: [u8; 8], /// Pad size for file name. pub x_fpad: [u8; 6], /// The source-file string type. pub x_ftype: u8, /// Reserved. pub x_freserve: [u8; 2], /// Specifies the type of auxiliary entry. Contains _AUX_FILE for this auxiliary entry. pub x_auxtype: u8, } // Values for `x_ftype`. // /// Specifies the source-file name. pub const XFT_FN: u8 = 0; /// Specifies the compiler time stamp. pub const XFT_CT: u8 = 1; /// Specifies the compiler version number. pub const XFT_CV: u8 = 2; /// Specifies compiler-defined information. pub const XFT_CD: u8 = 128; /// Csect auxiliary entry for C_EXT, C_WEAKEXT, and C_HIDEXT symbols. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct CsectAux32 { /// Section length. pub x_scnlen: U32, /// Offset of parameter type-check hash in .typchk section. pub x_parmhash: U32, /// .typchk section number. pub x_snhash: U16, /// Symbol alignment and type. pub x_smtyp: u8, /// Storage mapping class. pub x_smclas: u8, /// Reserved. pub x_stab: U32, /// x_snstab. pub x_snstab: U16, } /// Csect auxiliary entry for C_EXT, C_WEAKEXT, and C_HIDEXT symbols. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct CsectAux64 { /// Low 4 bytes of section length. pub x_scnlen_lo: U32, /// Offset of parameter type-check hash in .typchk section. pub x_parmhash: U32, /// .typchk section number. pub x_snhash: U16, /// Symbol alignment and type. pub x_smtyp: u8, /// Storage mapping class. pub x_smclas: u8, /// High 4 bytes of section length. pub x_scnlen_hi: U32, /// Reserved. pub pad: u8, /// Contains _AUX_CSECT; indicates type of auxiliary entry. pub x_auxtype: u8, } // Values for `x_smtyp`. // /// External reference. pub const XTY_ER: u8 = 0; /// Csect definition for initialized storage. pub const XTY_SD: u8 = 1; /// Defines an entry point to an initialized csect. pub const XTY_LD: u8 = 2; /// Common csect definition. For uninitialized storage. pub const XTY_CM: u8 = 3; // Values for `x_smclas`. // // READ ONLY CLASSES // /// Program Code pub const XMC_PR: u8 = 0; /// Read Only Constant pub const XMC_RO: u8 = 1; /// Debug Dictionary Table pub const XMC_DB: u8 = 2; /// Global Linkage (Interfile Interface Code) pub const XMC_GL: u8 = 6; /// Extended Operation (Pseudo Machine Instruction) pub const XMC_XO: u8 = 7; /// Supervisor Call (32-bit process only) pub const XMC_SV: u8 = 8; /// Supervisor Call for 64-bit process pub const XMC_SV64: u8 = 17; /// Supervisor Call for both 32- and 64-bit processes pub const XMC_SV3264: u8 = 18; /// Traceback Index csect pub const XMC_TI: u8 = 12; /// Traceback Table csect pub const XMC_TB: u8 = 13; // // READ WRITE CLASSES // /// Read Write Data pub const XMC_RW: u8 = 5; /// TOC Anchor for TOC Addressability pub const XMC_TC0: u8 = 15; /// General TOC item pub const XMC_TC: u8 = 3; /// Scalar data item in the TOC pub const XMC_TD: u8 = 16; /// Descriptor csect pub const XMC_DS: u8 = 10; /// Unclassified - Treated as Read Write pub const XMC_UA: u8 = 4; /// BSS class (uninitialized static internal) pub const XMC_BS: u8 = 9; /// Un-named Fortran Common pub const XMC_UC: u8 = 11; /// Initialized thread-local variable pub const XMC_TL: u8 = 20; /// Uninitialized thread-local variable pub const XMC_UL: u8 = 21; /// Symbol mapped at the end of TOC pub const XMC_TE: u8 = 22; /// Function auxiliary entry. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct FunAux32 { /// File offset to exception table entry. pub x_exptr: U32, /// Size of function in bytes. pub x_fsize: U32, /// File pointer to line number pub x_lnnoptr: U32, /// Symbol table index of next entry beyond this function. pub x_endndx: U32, /// Pad pub pad: U16, } /// Function auxiliary entry. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct FunAux64 { /// File pointer to line number pub x_lnnoptr: U64, /// Size of function in bytes. pub x_fsize: U32, /// Symbol table index of next entry beyond this function. pub x_endndx: U32, /// Pad pub pad: u8, /// Contains _AUX_FCN; Type of auxiliary entry. pub x_auxtype: u8, } /// Exception auxiliary entry. (XCOFF64 only) #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct ExpAux { /// File offset to exception table entry. pub x_exptr: U64, /// Size of function in bytes. pub x_fsize: U32, /// Symbol table index of next entry beyond this function. pub x_endndx: U32, /// Pad pub pad: u8, /// Contains _AUX_EXCEPT; Type of auxiliary entry pub x_auxtype: u8, } /// Block auxiliary entry for the C_BLOCK and C_FCN Symbols. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct BlockAux32 { /// Reserved. pub pad: [u8; 2], /// High-order 2 bytes of the source line number. pub x_lnnohi: U16, /// Low-order 2 bytes of the source line number. pub x_lnnolo: U16, /// Reserved. pub pad2: [u8; 12], } /// Block auxiliary entry for the C_BLOCK and C_FCN Symbols. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct BlockAux64 { /// Source line number. pub x_lnno: U32, /// Reserved. pub pad: [u8; 13], /// Contains _AUX_SYM; Type of auxiliary entry. pub x_auxtype: u8, } /// Section auxiliary entry for the C_STAT Symbol. (XCOFF32 Only) #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct StatAux { /// Section length. pub x_scnlen: U32, /// Number of relocation entries. pub x_nreloc: U16, /// Number of line numbers. pub x_nlinno: U16, /// Reserved. pub pad: [u8; 10], } /// Section auxiliary entry Format for C_DWARF symbols. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct DwarfAux32 { /// Length of portion of section represented by symbol. pub x_scnlen: U32, /// Reserved. pub pad: [u8; 4], /// Number of relocation entries in section. pub x_nreloc: U32, /// Reserved. pub pad2: [u8; 6], } /// Section auxiliary entry Format for C_DWARF symbols. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct DwarfAux64 { /// Length of portion of section represented by symbol. pub x_scnlen: U64, /// Number of relocation entries in section. pub x_nreloc: U64, /// Reserved. pub pad: u8, /// Contains _AUX_SECT; Type of Auxiliary entry. pub x_auxtype: u8, } // Values for `x_auxtype` // /// Identifies an exception auxiliary entry. pub const AUX_EXCEPT: u8 = 255; /// Identifies a function auxiliary entry. pub const AUX_FCN: u8 = 254; /// Identifies a symbol auxiliary entry. pub const AUX_SYM: u8 = 253; /// Identifies a file auxiliary entry. pub const AUX_FILE: u8 = 252; /// Identifies a csect auxiliary entry. pub const AUX_CSECT: u8 = 251; /// Identifies a SECT auxiliary entry. pub const AUX_SECT: u8 = 250; /// Relocation table entry #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Rel32 { /// Virtual address (position) in section to be relocated. pub r_vaddr: U32, /// Symbol table index of item that is referenced. pub r_symndx: U32, /// Relocation size and information. pub r_rsize: u8, /// Relocation type. pub r_rtype: u8, } /// Relocation table entry #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Rel64 { /// Virtual address (position) in section to be relocated. pub r_vaddr: U64, /// Symbol table index of item that is referenced. pub r_symndx: U32, /// Relocation size and information. pub r_rsize: u8, /// Relocation type. pub r_rtype: u8, } // Values for `r_rtype`. // /// Positive relocation. pub const R_POS: u8 = 0x00; /// Positive indirect load relocation. pub const R_RL: u8 = 0x0c; /// Positive load address relocation. Modifiable instruction. pub const R_RLA: u8 = 0x0d; /// Negative relocation. pub const R_NEG: u8 = 0x01; /// Relative to self relocation. pub const R_REL: u8 = 0x02; /// Relative to the TOC relocation. pub const R_TOC: u8 = 0x03; /// TOC relative indirect load relocation. pub const R_TRL: u8 = 0x12; /// Relative to the TOC or to the thread-local storage base relocation. pub const R_TRLA: u8 = 0x13; /// Global linkage-external TOC address relocation. pub const R_GL: u8 = 0x05; /// Local object TOC address relocation. pub const R_TCL: u8 = 0x06; /// A non-relocating relocation. pub const R_REF: u8 = 0x0f; /// Branch absolute relocation. References a non-modifiable instruction. pub const R_BA: u8 = 0x08; /// Branch relative to self relocation. References a non-modifiable instruction. pub const R_BR: u8 = 0x0a; /// Branch absolute relocation. References a modifiable instruction. pub const R_RBA: u8 = 0x18; /// Branch relative to self relocation. References a modifiable instruction. pub const R_RBR: u8 = 0x1a; /// General-dynamic reference to TLS symbol. pub const R_TLS: u8 = 0x20; /// Initial-exec reference to TLS symbol. pub const R_TLS_IE: u8 = 0x21; /// Local-dynamic reference to TLS symbol. pub const R_TLS_LD: u8 = 0x22; /// Local-exec reference to TLS symbol. pub const R_TLS_LE: u8 = 0x23; /// Module reference to TLS. pub const R_TLSM: u8 = 0x24; /// Module reference to the local TLS storage. pub const R_TLSML: u8 = 0x25; /// Relative to TOC upper. pub const R_TOCU: u8 = 0x30; /// Relative to TOC lower. pub const R_TOCL: u8 = 0x31; unsafe_impl_pod!( FileHeader32, FileHeader64, AuxHeader32, AuxHeader64, SectionHeader32, SectionHeader64, SymbolBytes, Symbol32, Symbol64, FileAux32, FileAux64, CsectAux32, CsectAux64, FunAux32, FunAux64, ExpAux, BlockAux32, BlockAux64, StatAux, DwarfAux32, DwarfAux64, Rel32, Rel64, ); object-0.36.5/tests/build/elf.rs000064400000000000000000000213671046102023000145530ustar 00000000000000use object::{build, elf}; // Test that offset 0 is supported for SHT_NOBITS sections. #[test] fn test_nobits_offset() { let mut builder = build::elf::Builder::new(object::Endianness::Little, true); builder.header.e_type = elf::ET_EXEC; builder.header.e_phoff = 0x40; let section = builder.sections.add(); section.name = b".shstrtab"[..].into(); section.sh_type = elf::SHT_STRTAB; section.data = build::elf::SectionData::SectionString; let section = builder.sections.add(); section.name = b".bss"[..].into(); section.sh_type = elf::SHT_NOBITS; section.sh_flags = (elf::SHF_ALLOC | elf::SHF_WRITE) as u64; section.sh_addr = 0x1000; section.sh_offset = 0; section.sh_size = 0x1000; section.sh_addralign = 16; section.data = build::elf::SectionData::UninitializedData(0x1000); let section_id = section.id(); let segment = builder.segments.add(); segment.p_type = elf::PT_LOAD; segment.p_flags = elf::PF_R | elf::PF_W; segment.p_offset = 0x1000; segment.p_vaddr = 0x1000; segment.p_paddr = 0x1000; segment.p_filesz = 0; segment.p_memsz = 0x1000; segment.p_align = 16; segment.sections.push(section_id); let mut buf = Vec::new(); builder.write(&mut buf).unwrap(); } // Test that we can read and write a file with no dynamic string table. #[test] fn test_no_dynstr() { let mut builder = build::elf::Builder::new(object::Endianness::Little, true); builder.header.e_type = elf::ET_EXEC; builder.header.e_machine = elf::EM_X86_64; builder.header.e_phoff = 0x40; let section = builder.sections.add(); section.name = b".shstrtab"[..].into(); section.sh_type = elf::SHT_STRTAB; section.data = build::elf::SectionData::SectionString; let section = builder.sections.add(); section.name = b".dynsym"[..].into(); section.sh_type = elf::SHT_DYNSYM; section.sh_flags = elf::SHF_ALLOC as u64; section.sh_addralign = 8; section.data = build::elf::SectionData::DynamicSymbol; let dynsym_id = section.id(); let section = builder.sections.add(); section.name = b".rela.dyn"[..].into(); section.sh_type = elf::SHT_RELA; section.sh_flags = elf::SHF_ALLOC as u64; section.sh_addralign = 8; section.data = build::elf::SectionData::DynamicRelocation(vec![build::elf::DynamicRelocation { r_offset: 0x1000, symbol: None, r_type: elf::R_X86_64_64, r_addend: 0x300, }]); let rela_id = section.id(); builder.set_section_sizes(); let segment = builder.segments.add(); segment.p_type = elf::PT_LOAD; segment.p_flags = elf::PF_R; segment.p_filesz = 0x1000; segment.p_memsz = 0x1000; segment.p_align = 8; segment.append_section(builder.sections.get_mut(dynsym_id)); segment.append_section(builder.sections.get_mut(rela_id)); let mut buf = Vec::new(); builder.write(&mut buf).unwrap(); let builder = build::elf::Builder::read(&*buf).unwrap(); assert_eq!(builder.sections.count(), 3); assert_eq!(builder.segments.count(), 1); for section in &builder.sections { match §ion.data { build::elf::SectionData::DynamicSymbol => { assert_eq!(section.sh_offset, 0x1000); } build::elf::SectionData::DynamicRelocation(rela) => { assert_eq!(section.sh_offset, 0x1018); assert_eq!(rela.len(), 1); } _ => {} } } } #[test] fn test_attribute() { let mut builder = build::elf::Builder::new(object::Endianness::Little, true); builder.header.e_type = elf::ET_EXEC; builder.header.e_machine = elf::EM_X86_64; builder.header.e_phoff = 0x40; let section = builder.sections.add(); section.name = b".shstrtab"[..].into(); section.sh_type = elf::SHT_STRTAB; section.data = build::elf::SectionData::SectionString; let attributes = build::elf::AttributesSection { subsections: vec![build::elf::AttributesSubsection { vendor: b"GNU"[..].into(), subsubsections: vec![ (build::elf::AttributesSubsubsection { tag: build::elf::AttributeTag::File, data: b"123"[..].into(), }), ], }], }; let section = builder.sections.add(); section.name = b".gnu.attributes"[..].into(); section.sh_type = elf::SHT_GNU_ATTRIBUTES; section.sh_addralign = 8; section.data = build::elf::SectionData::Attributes(attributes); let mut buf = Vec::new(); builder.write(&mut buf).unwrap(); let builder = build::elf::Builder::read(&*buf).unwrap(); assert_eq!(builder.sections.count(), 2); for section in &builder.sections { if let build::elf::SectionData::Attributes(attributes) = §ion.data { assert_eq!(attributes.subsections.len(), 1); assert_eq!(attributes.subsections[0].vendor.as_slice(), b"GNU"); assert_eq!(attributes.subsections[0].subsubsections.len(), 1); assert_eq!( attributes.subsections[0].subsubsections[0].tag, build::elf::AttributeTag::File ); assert_eq!( attributes.subsections[0].subsubsections[0].data.as_slice(), b"123" ); } } } #[test] fn test_dynsym() { let mut builder = build::elf::Builder::new(object::Endianness::Little, true); builder.header.e_type = elf::ET_EXEC; builder.header.e_machine = elf::EM_X86_64; builder.header.e_phoff = 0x40; let section = builder.sections.add(); section.name = b".shstrtab"[..].into(); section.sh_type = elf::SHT_STRTAB; section.data = build::elf::SectionData::SectionString; let section = builder.sections.add(); section.name = b".text"[..].into(); section.sh_type = elf::SHT_PROGBITS; section.sh_flags = (elf::SHF_ALLOC | elf::SHF_EXECINSTR) as u64; section.sh_addralign = 16; section.data = build::elf::SectionData::Data(vec![0xcc; 100].into()); let text_id = section.id(); let section = builder.sections.add(); section.name = b".dynsym"[..].into(); section.sh_type = elf::SHT_DYNSYM; section.sh_flags = elf::SHF_ALLOC as u64; section.sh_addralign = 8; section.data = build::elf::SectionData::DynamicSymbol; let dynsym_id = section.id(); let section = builder.sections.add(); section.name = b".dynstr"[..].into(); section.sh_type = elf::SHT_STRTAB; section.sh_flags = elf::SHF_ALLOC as u64; section.sh_addralign = 1; section.data = build::elf::SectionData::DynamicString; let dynstr_id = section.id(); let section = builder.sections.add(); section.name = b".gnu.hash"[..].into(); section.sh_type = elf::SHT_GNU_HASH; section.sh_flags = elf::SHF_ALLOC as u64; section.sh_addralign = 8; section.data = build::elf::SectionData::GnuHash; let gnu_hash_id = section.id(); builder.gnu_hash_bloom_shift = 1; builder.gnu_hash_bloom_count = 1; builder.gnu_hash_bucket_count = 1; let symbol = builder.dynamic_symbols.add(); symbol.name = b"global"[..].into(); symbol.set_st_info(elf::STB_GLOBAL, elf::STT_FUNC); symbol.section = Some(text_id); let symbol = builder.dynamic_symbols.add(); symbol.name = b"undefined"[..].into(); symbol.set_st_info(elf::STB_GLOBAL, elf::STT_NOTYPE); let symbol = builder.dynamic_symbols.add(); symbol.name = b"local"[..].into(); symbol.set_st_info(elf::STB_LOCAL, elf::STT_FUNC); symbol.section = Some(text_id); builder.set_section_sizes(); let segment = builder.segments.add(); segment.p_type = elf::PT_LOAD; segment.p_flags = elf::PF_R; segment.p_filesz = 0x1000; segment.p_memsz = 0x1000; segment.p_align = 8; segment.append_section(builder.sections.get_mut(text_id)); segment.append_section(builder.sections.get_mut(dynsym_id)); segment.append_section(builder.sections.get_mut(dynstr_id)); segment.append_section(builder.sections.get_mut(gnu_hash_id)); let mut buf = Vec::new(); builder.write(&mut buf).unwrap(); let builder = build::elf::Builder::read(&*buf).unwrap(); assert_eq!(builder.sections.count(), 5); assert_eq!(builder.dynamic_symbols.count(), 3); // Check that the dynamic symbol table sorting handles // local and undefined symbols correctly. assert_eq!( builder .dynamic_symbols .iter() .map(|s| s.name.as_slice()) .collect::>(), vec![&b"local"[..], &b"undefined"[..], &b"global"[..]] ); for section in &builder.sections { if let build::elf::SectionData::DynamicSymbol = §ion.data { // Check that sh_info includes the number of local symbols. assert_eq!(section.sh_info, 2); } } } object-0.36.5/tests/build/mod.rs000064400000000000000000000000451046102023000145520ustar 00000000000000#![cfg(feature = "build")] mod elf; object-0.36.5/tests/integration.rs000064400000000000000000000000451046102023000152170ustar 00000000000000mod build; mod read; mod round_trip; object-0.36.5/tests/parse_self.rs000064400000000000000000000013151046102023000150200ustar 00000000000000#![cfg(feature = "read")] use object::{File, Object}; use std::{env, fs}; #[test] fn parse_self() { let exe = env::current_exe().unwrap(); let data = fs::read(exe).unwrap(); let object = File::parse(&*data).unwrap(); assert!(object.entry() != 0); assert!(object.sections().count() != 0); } #[cfg(feature = "std")] #[test] fn parse_self_cache() { use object::read::{ReadCache, ReadRef}; let exe = env::current_exe().unwrap(); let file = fs::File::open(exe).unwrap(); let cache = ReadCache::new(file); let data = cache.range(0, cache.len().unwrap()); let object = File::parse(data).unwrap(); assert!(object.entry() != 0); assert!(object.sections().count() != 0); } object-0.36.5/tests/read/coff.rs000064400000000000000000000016411046102023000145270ustar 00000000000000use object::{pe, read, Object, ObjectSection}; use std::fs; use std::path::PathBuf; #[cfg(feature = "coff")] #[test] fn coff_extended_relocations() { let path_to_obj: PathBuf = ["testfiles", "coff", "relocs_overflow.o"].iter().collect(); let contents = fs::read(path_to_obj).expect("Could not read relocs_overflow.o"); let file = read::coff::CoffFile::<_>::parse(&contents[..]).expect("Could not parse relocs_overflow.o"); let code_section = file .section_by_name(".text") .expect("Could not find .text section in relocs_overflow.o"); match code_section.flags() { object::SectionFlags::Coff { characteristics } => { assert!(characteristics & pe::IMAGE_SCN_LNK_NRELOC_OVFL != 0) } _ => panic!("Invalid section flags flavour."), }; let relocations = code_section.relocations().collect::>(); assert_eq!(relocations.len(), 65536); } object-0.36.5/tests/read/elf.rs000064400000000000000000000024101046102023000143530ustar 00000000000000#[cfg(feature = "std")] use std::path::{Path, PathBuf}; #[cfg(feature = "std")] fn get_buildid(path: &Path) -> Result>, object::read::Error> { use object::Object; let file = std::fs::File::open(path).unwrap(); let reader = object::read::ReadCache::new(file); let object = object::read::File::parse(&reader)?; object .build_id() .map(|option| option.map(ToOwned::to_owned)) } #[cfg(feature = "std")] #[test] /// Regression test: used to attempt to allocate 5644418395173552131 bytes fn get_buildid_bad_elf() { let path: PathBuf = [ "testfiles", "elf", "yara-fuzzing", "crash-7dc27920ae1cb85333e7f2735a45014488134673", ] .iter() .collect(); let _ = get_buildid(&path); } #[cfg(feature = "std")] #[test] fn get_buildid_less_bad_elf() { let path: PathBuf = [ "testfiles", "elf", "yara-fuzzing", "crash-f1fd008da535b110853885221ebfaac3f262a1c1e280f10929f7b353c44996c8", ] .iter() .collect(); let buildid = get_buildid(&path).unwrap().unwrap(); // ground truth obtained from GNU binutils's readelf assert_eq!( buildid, b"\xf9\xc0\xc6\x05\xd3\x76\xbb\xa5\x7e\x02\xf5\x74\x50\x9d\x16\xcc\xe9\x9c\x1b\xf1" ); } object-0.36.5/tests/read/macho.rs000064400000000000000000000037061046102023000147050ustar 00000000000000#[cfg(feature = "std")] use object::{Object, ObjectSection as _}; // Test that we can read compressed sections in Mach-O files as produced // by the Go compiler. #[cfg(feature = "std")] #[test] fn test_go_macho() { let macho_testfiles = std::path::Path::new("testfiles/macho"); // Section names we expect to find, whether they should be // compressed, and the actual name of the section in the file. const EXPECTED: &[(&str, bool, &str)] = &[ (".debug_abbrev", true, "__zdebug_abbrev"), (".debug_gdb_scripts", false, "__debug_gdb_scri"), (".debug_ranges", true, "__zdebug_ranges"), ("__data", false, "__data"), ]; for file in &["go-aarch64", "go-x86_64"] { let path = macho_testfiles.join(file); let file = std::fs::File::open(path).unwrap(); let reader = object::read::ReadCache::new(file); let object = object::read::File::parse(&reader).unwrap(); for &(name, compressed, actual_name) in EXPECTED { let section = object.section_by_name(name).unwrap(); assert_eq!(section.name(), Ok(actual_name)); let compressed_file_range = section.compressed_file_range().unwrap(); let size = section.size(); if compressed { assert_eq!( compressed_file_range.format, object::CompressionFormat::Zlib ); assert_eq!(compressed_file_range.compressed_size, size - 12); assert!( compressed_file_range.uncompressed_size > compressed_file_range.compressed_size, "decompressed size is greater than compressed size" ); } else { assert_eq!( compressed_file_range.format, object::CompressionFormat::None ); assert_eq!(compressed_file_range.compressed_size, size); } } } } object-0.36.5/tests/read/mod.rs000064400000000000000000000000711046102023000143650ustar 00000000000000#![cfg(feature = "read")] mod coff; mod elf; mod macho; object-0.36.5/tests/round_trip/bss.rs000064400000000000000000000202321046102023000156500ustar 00000000000000#![cfg(all(feature = "read", feature = "write"))] use object::read::{Object, ObjectSection, ObjectSymbol}; use object::{read, write}; use object::{ Architecture, BinaryFormat, Endianness, SectionKind, SymbolFlags, SymbolKind, SymbolScope, }; #[test] fn coff_x86_64_bss() { let mut object = write::Object::new(BinaryFormat::Coff, Architecture::X86_64, Endianness::Little); let section = object.section_id(write::StandardSection::UninitializedData); let _bss_section_symbol = object.section_symbol(section); let symbol = object.add_symbol(write::Symbol { name: b"v1".to_vec(), value: 0, size: 0, kind: SymbolKind::Data, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Undefined, flags: SymbolFlags::None, }); object.add_symbol_bss(symbol, section, 18, 4); let symbol = object.add_symbol(write::Symbol { name: b"v2".to_vec(), value: 0, size: 0, kind: SymbolKind::Data, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Undefined, flags: SymbolFlags::None, }); object.add_symbol_bss(symbol, section, 34, 8); let bytes = object.write().unwrap(); //std::fs::write(&"bss.o", &bytes).unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::Coff); assert_eq!(object.architecture(), Architecture::X86_64); let mut sections = object.sections(); let bss = sections.next().unwrap(); println!("{:?}", bss); let bss_index = bss.index(); assert_eq!(bss.name(), Ok(".bss")); assert_eq!(bss.kind(), SectionKind::UninitializedData); assert_eq!(bss.size(), 58); assert_eq!(bss.data(), Ok(&[][..])); let section = sections.next(); assert!(section.is_none(), "unexpected section {:?}", section); let mut symbols = object.symbols(); let section_symbol = symbols.next().unwrap(); println!("{:?}", section_symbol); assert_eq!(section_symbol.name(), Ok(".bss")); assert_eq!(section_symbol.kind(), SymbolKind::Section); assert_eq!(section_symbol.section_index(), Some(bss_index)); assert_eq!(section_symbol.scope(), SymbolScope::Compilation); assert!(!section_symbol.is_weak()); assert!(!section_symbol.is_undefined()); assert_eq!(section_symbol.address(), 0); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok("v1")); assert_eq!(symbol.kind(), SymbolKind::Data); assert_eq!(symbol.section_index(), Some(bss_index)); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); assert_eq!(symbol.address(), 0); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok("v2")); assert_eq!(symbol.kind(), SymbolKind::Data); assert_eq!(symbol.section_index(), Some(bss_index)); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); assert_eq!(symbol.address(), 24); let symbol = symbols.next(); assert!(symbol.is_none(), "unexpected symbol {:?}", symbol); } #[test] fn elf_x86_64_bss() { let mut object = write::Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); let section = object.section_id(write::StandardSection::UninitializedData); let symbol = object.add_symbol(write::Symbol { name: b"v1".to_vec(), value: 0, size: 0, kind: SymbolKind::Data, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Undefined, flags: SymbolFlags::None, }); object.add_symbol_bss(symbol, section, 18, 4); let symbol = object.add_symbol(write::Symbol { name: b"v2".to_vec(), value: 0, size: 0, kind: SymbolKind::Data, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Undefined, flags: SymbolFlags::None, }); object.add_symbol_bss(symbol, section, 34, 8); let bytes = object.write().unwrap(); //std::fs::write(&"bss.o", &bytes).unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::Elf); assert_eq!(object.architecture(), Architecture::X86_64); let mut sections = object.sections(); let bss = sections.next().unwrap(); println!("{:?}", bss); let bss_index = bss.index(); assert_eq!(bss.name(), Ok(".bss")); assert_eq!(bss.kind(), SectionKind::UninitializedData); assert_eq!(bss.size(), 58); assert_eq!(bss.data(), Ok(&[][..])); let mut symbols = object.symbols(); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok("v1")); assert_eq!(symbol.kind(), SymbolKind::Data); assert_eq!(symbol.section_index(), Some(bss_index)); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); assert_eq!(symbol.address(), 0); assert_eq!(symbol.size(), 18); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok("v2")); assert_eq!(symbol.kind(), SymbolKind::Data); assert_eq!(symbol.section_index(), Some(bss_index)); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); assert_eq!(symbol.address(), 24); assert_eq!(symbol.size(), 34); let symbol = symbols.next(); assert!(symbol.is_none(), "unexpected symbol {:?}", symbol); } #[test] fn macho_x86_64_bss() { let mut object = write::Object::new( BinaryFormat::MachO, Architecture::X86_64, Endianness::Little, ); let section = object.section_id(write::StandardSection::UninitializedData); let symbol = object.add_symbol(write::Symbol { name: b"v1".to_vec(), value: 0, size: 0, kind: SymbolKind::Data, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Undefined, flags: SymbolFlags::None, }); object.add_symbol_bss(symbol, section, 18, 4); let symbol = object.add_symbol(write::Symbol { name: b"v2".to_vec(), value: 0, size: 0, kind: SymbolKind::Data, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Undefined, flags: SymbolFlags::None, }); object.add_symbol_bss(symbol, section, 34, 8); let bytes = object.write().unwrap(); //std::fs::write(&"bss.o", &bytes).unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::MachO); assert_eq!(object.architecture(), Architecture::X86_64); let mut sections = object.sections(); let bss = sections.next().unwrap(); println!("{:?}", bss); let bss_index = bss.index(); assert_eq!(bss.name(), Ok("__bss")); assert_eq!(bss.segment_name(), Ok(Some("__DATA"))); assert_eq!(bss.kind(), SectionKind::UninitializedData); assert_eq!(bss.size(), 58); assert_eq!(bss.data(), Ok(&[][..])); let section = sections.next(); assert!(section.is_none(), "unexpected section {:?}", section); let mut symbols = object.symbols(); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok("_v1")); assert_eq!(symbol.kind(), SymbolKind::Data); assert_eq!(symbol.section_index(), Some(bss_index)); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); assert_eq!(symbol.address(), 0); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok("_v2")); assert_eq!(symbol.kind(), SymbolKind::Data); assert_eq!(symbol.section_index(), Some(bss_index)); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); assert_eq!(symbol.address(), 24); let symbol = symbols.next(); assert!(symbol.is_none(), "unexpected symbol {:?}", symbol); } object-0.36.5/tests/round_trip/coff.rs000064400000000000000000000034371046102023000160060ustar 00000000000000use object::read::{Object, ObjectSection}; use object::{read, write}; use object::{ Architecture, BinaryFormat, Endianness, RelocationEncoding, RelocationFlags, RelocationKind, SymbolFlags, SymbolKind, SymbolScope, }; #[test] fn reloc_overflow() { let mut object = write::Object::new(BinaryFormat::Coff, Architecture::X86_64, Endianness::Little); let text = object.section_id(write::StandardSection::Text); object.append_section_data(text, &[0; 4], 4); let symbol = object.add_symbol(write::Symbol { name: b"f".to_vec(), value: 0, size: 4, kind: SymbolKind::Text, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Section(text), flags: SymbolFlags::None, }); for i in 0..0x10000 { object .add_relocation( text, write::Relocation { offset: i, symbol, addend: 0, flags: RelocationFlags::Generic { kind: RelocationKind::Absolute, encoding: RelocationEncoding::Generic, size: 64, }, }, ) .unwrap(); } let bytes = object.write().unwrap(); //std::fs::write(&"reloc_overflow.o", &bytes).unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::Coff); assert_eq!(object.architecture(), Architecture::X86_64); let section = object.sections().next().unwrap(); assert_eq!(section.name(), Ok(".text")); let mut i = 0; for (offset, _relocation) in section.relocations() { assert_eq!(offset, i); i += 1; } assert_eq!(i, 0x10000); } object-0.36.5/tests/round_trip/comdat.rs000064400000000000000000000157011046102023000163350ustar 00000000000000#![cfg(all(feature = "read", feature = "write"))] use object::pe; use object::read::{Object, ObjectComdat, ObjectSection, ObjectSymbol}; use object::{read, write}; use object::{ Architecture, BinaryFormat, ComdatKind, Endianness, SectionKind, SymbolFlags, SymbolKind, SymbolScope, }; #[test] fn coff_x86_64_comdat() { let mut object = write::Object::new(BinaryFormat::Coff, Architecture::X86_64, Endianness::Little); let section1 = object.add_subsection(write::StandardSection::Text, b"s1"); let offset = object.append_section_data(section1, &[0, 1, 2, 3], 4); object.section_symbol(section1); let section2 = object.add_subsection(write::StandardSection::Data, b"s1"); object.append_section_data(section2, &[0, 1, 2, 3], 4); object.section_symbol(section2); let symbol = object.add_symbol(write::Symbol { name: b"s1".to_vec(), value: offset, size: 4, kind: SymbolKind::Data, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Section(section1), flags: SymbolFlags::None, }); object.add_comdat(write::Comdat { kind: ComdatKind::NoDuplicates, symbol, sections: vec![section1, section2], }); let bytes = object.write().unwrap(); //std::fs::write(&"comdat.o", &bytes).unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::Coff); assert_eq!(object.architecture(), Architecture::X86_64); let mut sections = object.sections(); let section1 = sections.next().unwrap(); println!("{:?}", section1); let section1_index = section1.index(); assert_eq!(section1.name(), Ok(".text$s1")); assert_eq!(section1.kind(), SectionKind::Text); assert_eq!(section1.address(), 0); assert_eq!(section1.size(), 4); let section2 = sections.next().unwrap(); println!("{:?}", section2); let section2_index = section2.index(); assert_eq!(section2.name(), Ok(".data$s1")); assert_eq!(section2.kind(), SectionKind::Data); assert_eq!(section2.address(), 0); assert_eq!(section2.size(), 4); let mut symbols = object.symbols(); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok(".text$s1")); assert_eq!(symbol.kind(), SymbolKind::Section); assert_eq!( symbol.section(), read::SymbolSection::Section(section1.index()) ); assert_eq!( symbol.flags(), SymbolFlags::CoffSection { selection: pe::IMAGE_COMDAT_SELECT_NODUPLICATES, associative_section: None } ); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok(".data$s1")); assert_eq!(symbol.kind(), SymbolKind::Section); assert_eq!( symbol.section(), read::SymbolSection::Section(section2.index()) ); assert_eq!( symbol.flags(), SymbolFlags::CoffSection { selection: pe::IMAGE_COMDAT_SELECT_ASSOCIATIVE, associative_section: Some(section1_index) } ); let symbol = symbols.next().unwrap(); let symbol_index = symbol.index(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok("s1")); assert_eq!(symbol.kind(), SymbolKind::Data); assert_eq!( symbol.section(), read::SymbolSection::Section(section1.index()) ); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); assert_eq!(symbol.address(), 0); let symbol = symbols.next(); assert!(symbol.is_none(), "unexpected symbol {:?}", symbol); let mut comdats = object.comdats(); let comdat = comdats.next().unwrap(); println!("{:?}", comdat); assert_eq!(comdat.kind(), ComdatKind::NoDuplicates); assert_eq!(comdat.symbol(), symbol_index); let mut comdat_sections = comdat.sections(); assert_eq!(comdat_sections.next(), Some(section1_index)); assert_eq!(comdat_sections.next(), Some(section2_index)); assert_eq!(comdat_sections.next(), None); } #[test] fn elf_x86_64_comdat() { let mut object = write::Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); let section1 = object.add_subsection(write::StandardSection::Text, b"s1"); let offset = object.append_section_data(section1, &[0, 1, 2, 3], 4); let section2 = object.add_subsection(write::StandardSection::Data, b"s1"); object.append_section_data(section2, &[0, 1, 2, 3], 4); let symbol = object.add_symbol(write::Symbol { name: b"s1".to_vec(), value: offset, size: 4, kind: SymbolKind::Data, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Section(section1), flags: SymbolFlags::None, }); object.add_comdat(write::Comdat { kind: ComdatKind::Any, symbol, sections: vec![section1, section2], }); let bytes = object.write().unwrap(); //std::fs::write(&"comdat.o", &bytes).unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::Elf); assert_eq!(object.architecture(), Architecture::X86_64); let mut sections = object.sections(); let section = sections.next().unwrap(); println!("{:?}", section); assert_eq!(section.name(), Ok(".group")); let section1 = sections.next().unwrap(); println!("{:?}", section1); let section1_index = section1.index(); assert_eq!(section1.name(), Ok(".text.s1")); assert_eq!(section1.kind(), SectionKind::Text); assert_eq!(section1.address(), 0); assert_eq!(section1.size(), 4); let section2 = sections.next().unwrap(); println!("{:?}", section2); let section2_index = section2.index(); assert_eq!(section2.name(), Ok(".data.s1")); assert_eq!(section2.kind(), SectionKind::Data); assert_eq!(section2.address(), 0); assert_eq!(section2.size(), 4); let mut symbols = object.symbols(); let symbol = symbols.next().unwrap(); let symbol_index = symbol.index(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok("s1")); assert_eq!(symbol.kind(), SymbolKind::Data); assert_eq!( symbol.section(), read::SymbolSection::Section(section1.index()) ); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); assert_eq!(symbol.address(), 0); let symbol = symbols.next(); assert!(symbol.is_none(), "unexpected symbol {:?}", symbol); let mut comdats = object.comdats(); let comdat = comdats.next().unwrap(); println!("{:?}", comdat); assert_eq!(comdat.kind(), ComdatKind::Any); assert_eq!(comdat.symbol(), symbol_index); let mut comdat_sections = comdat.sections(); assert_eq!(comdat_sections.next(), Some(section1_index)); assert_eq!(comdat_sections.next(), Some(section2_index)); assert_eq!(comdat_sections.next(), None); } object-0.36.5/tests/round_trip/common.rs000064400000000000000000000166711046102023000163650ustar 00000000000000#![cfg(all(feature = "read", feature = "write"))] use object::read::{Object, ObjectSection, ObjectSymbol}; use object::{read, write}; use object::{ Architecture, BinaryFormat, Endianness, SectionKind, SymbolFlags, SymbolKind, SymbolScope, }; #[test] fn coff_x86_64_common() { let mut object = write::Object::new(BinaryFormat::Coff, Architecture::X86_64, Endianness::Little); let symbol = write::Symbol { name: b"v1".to_vec(), value: 0, size: 0, kind: SymbolKind::Data, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Undefined, flags: SymbolFlags::None, }; object.add_common_symbol(symbol, 4, 4); let symbol = write::Symbol { name: b"v2".to_vec(), value: 0, size: 0, kind: SymbolKind::Data, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Undefined, flags: SymbolFlags::None, }; object.add_common_symbol(symbol, 8, 8); // Also check undefined symbols, which are very similar. let symbol = write::Symbol { name: b"v3".to_vec(), value: 0, size: 0, kind: SymbolKind::Data, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Undefined, flags: SymbolFlags::None, }; object.add_symbol(symbol); let bytes = object.write().unwrap(); //std::fs::write(&"common.o", &bytes).unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::Coff); assert_eq!(object.architecture(), Architecture::X86_64); let mut symbols = object.symbols(); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok("v1")); assert_eq!(symbol.kind(), SymbolKind::Data); assert_eq!(symbol.section(), read::SymbolSection::Common); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); assert_eq!(symbol.address(), 0); assert_eq!(symbol.size(), 4); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok("v2")); assert_eq!(symbol.kind(), SymbolKind::Data); assert_eq!(symbol.section(), read::SymbolSection::Common); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); assert_eq!(symbol.address(), 0); assert_eq!(symbol.size(), 8); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok("v3")); assert_eq!(symbol.kind(), SymbolKind::Data); assert_eq!(symbol.section(), read::SymbolSection::Undefined); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(symbol.is_undefined()); assert_eq!(symbol.address(), 0); assert_eq!(symbol.size(), 0); let symbol = symbols.next(); assert!(symbol.is_none(), "unexpected symbol {:?}", symbol); } #[test] fn elf_x86_64_common() { let mut object = write::Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); let symbol = write::Symbol { name: b"v1".to_vec(), value: 0, size: 0, kind: SymbolKind::Data, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Undefined, flags: SymbolFlags::None, }; object.add_common_symbol(symbol, 4, 4); let symbol = write::Symbol { name: b"v2".to_vec(), value: 0, size: 0, kind: SymbolKind::Data, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Undefined, flags: SymbolFlags::None, }; object.add_common_symbol(symbol, 8, 8); let bytes = object.write().unwrap(); //std::fs::write(&"common.o", &bytes).unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::Elf); assert_eq!(object.architecture(), Architecture::X86_64); let mut symbols = object.symbols(); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok("v1")); assert_eq!(symbol.kind(), SymbolKind::Data); assert_eq!(symbol.section(), read::SymbolSection::Common); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); assert_eq!(symbol.address(), 0); assert_eq!(symbol.size(), 4); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok("v2")); assert_eq!(symbol.kind(), SymbolKind::Data); assert_eq!(symbol.section(), read::SymbolSection::Common); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); assert_eq!(symbol.address(), 0); assert_eq!(symbol.size(), 8); let symbol = symbols.next(); assert!(symbol.is_none(), "unexpected symbol {:?}", symbol); } #[test] fn macho_x86_64_common() { let mut object = write::Object::new( BinaryFormat::MachO, Architecture::X86_64, Endianness::Little, ); let symbol = write::Symbol { name: b"v1".to_vec(), value: 0, size: 0, kind: SymbolKind::Data, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Undefined, flags: SymbolFlags::None, }; object.add_common_symbol(symbol, 4, 4); let symbol = write::Symbol { name: b"v2".to_vec(), value: 0, size: 0, kind: SymbolKind::Data, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Undefined, flags: SymbolFlags::None, }; object.add_common_symbol(symbol, 8, 8); let bytes = object.write().unwrap(); //std::fs::write(&"common.o", &bytes).unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::MachO); assert_eq!(object.architecture(), Architecture::X86_64); let mut sections = object.sections(); let common = sections.next().unwrap(); println!("{:?}", common); let common_index = common.index(); assert_eq!(common.name(), Ok("__common")); assert_eq!(common.segment_name(), Ok(Some("__DATA"))); assert_eq!(common.kind(), SectionKind::Common); assert_eq!(common.size(), 16); assert_eq!(common.data(), Ok(&[][..])); let section = sections.next(); assert!(section.is_none(), "unexpected section {:?}", section); let mut symbols = object.symbols(); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok("_v1")); assert_eq!(symbol.kind(), SymbolKind::Data); assert_eq!(symbol.section_index(), Some(common_index)); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); assert_eq!(symbol.address(), 0); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok("_v2")); assert_eq!(symbol.kind(), SymbolKind::Data); assert_eq!(symbol.section_index(), Some(common_index)); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); assert_eq!(symbol.address(), 8); let symbol = symbols.next(); assert!(symbol.is_none(), "unexpected symbol {:?}", symbol); } object-0.36.5/tests/round_trip/elf.rs000064400000000000000000000252071046102023000156360ustar 00000000000000use object::read::elf::{FileHeader, SectionHeader}; use object::read::{Object, ObjectSection, ObjectSymbol}; use object::{ elf, read, write, Architecture, BinaryFormat, Endianness, LittleEndian, SectionIndex, SectionKind, SymbolFlags, SymbolKind, SymbolScope, SymbolSection, U32, }; use std::io::Write; #[test] fn symtab_shndx() { let mut object = write::Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); for i in 0..0x10000 { let name = format!("func{}", i).into_bytes(); let section = object.add_subsection(write::StandardSection::Text, &name); let offset = object.append_section_data(section, &[0xcc], 1); object.add_symbol(write::Symbol { name, value: offset, size: 1, kind: SymbolKind::Text, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Section(section), flags: SymbolFlags::None, }); } let bytes = object.write().unwrap(); //std::fs::write(&"symtab_shndx.o", &bytes).unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::Elf); assert_eq!(object.architecture(), Architecture::X86_64); for symbol in object.symbols() { assert_eq!( symbol.section(), SymbolSection::Section(SectionIndex(symbol.index().0)) ); } } #[test] fn empty_symtab() { let object = write::Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); let bytes = object.write().unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::Elf); assert_eq!(object.architecture(), Architecture::X86_64); let symtab = object.section_by_name(".symtab").unwrap(); assert_eq!(symtab.size(), 24); let strtab = object.section_by_name(".strtab").unwrap(); assert_eq!(strtab.size(), 1); } #[test] fn aligned_sections() { let mut object = write::Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); let text_section_id = object.add_section(vec![], b".text".to_vec(), SectionKind::Text); let text_section = object.section_mut(text_section_id); text_section.set_data(&[][..], 4096); let data_section_id = object.add_section(vec![], b".data".to_vec(), SectionKind::Data); let data_section = object.section_mut(data_section_id); data_section.set_data(&b"1234"[..], 16); let bytes = object.write().unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::Elf); assert_eq!(object.architecture(), Architecture::X86_64); let mut sections = object.sections(); let section = sections.next().unwrap(); assert_eq!(section.name(), Ok(".text")); assert_eq!(section.file_range(), Some((4096, 0))); let section = sections.next().unwrap(); assert_eq!(section.name(), Ok(".data")); assert_eq!(section.file_range(), Some((4096, 4))); } #[cfg(feature = "compression")] #[test] fn compression_zlib() { use object::read::ObjectSection; use object::LittleEndian as LE; let data = b"test data data data"; let len = data.len() as u64; let mut ch = object::elf::CompressionHeader64::::default(); ch.ch_type.set(LE, object::elf::ELFCOMPRESS_ZLIB); ch.ch_size.set(LE, len); ch.ch_addralign.set(LE, 1); let mut buf = Vec::new(); buf.write_all(object::bytes_of(&ch)).unwrap(); let mut encoder = flate2::write::ZlibEncoder::new(buf, flate2::Compression::default()); encoder.write_all(data).unwrap(); let compressed = encoder.finish().unwrap(); let mut object = write::Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); let section = object.add_section( Vec::new(), b".debug_info".to_vec(), object::SectionKind::Other, ); object.section_mut(section).set_data(compressed, 1); object.section_mut(section).flags = object::SectionFlags::Elf { sh_flags: object::elf::SHF_COMPRESSED.into(), }; let bytes = object.write().unwrap(); //std::fs::write(&"compression.o", &bytes).unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::Elf); assert_eq!(object.architecture(), Architecture::X86_64); let section = object.section_by_name(".debug_info").unwrap(); let uncompressed = section.uncompressed_data().unwrap(); assert_eq!(data, &*uncompressed); } #[cfg(feature = "compression")] #[test] fn compression_gnu() { use object::read::ObjectSection; use std::io::Write; let data = b"test data data data"; let len = data.len() as u32; let mut buf = Vec::new(); buf.write_all(b"ZLIB\0\0\0\0").unwrap(); buf.write_all(&len.to_be_bytes()).unwrap(); let mut encoder = flate2::write::ZlibEncoder::new(buf, flate2::Compression::default()); encoder.write_all(data).unwrap(); let compressed = encoder.finish().unwrap(); let mut object = write::Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); let section = object.add_section( Vec::new(), b".zdebug_info".to_vec(), object::SectionKind::Other, ); object.section_mut(section).set_data(compressed, 1); let bytes = object.write().unwrap(); //std::fs::write(&"compression.o", &bytes).unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::Elf); assert_eq!(object.architecture(), Architecture::X86_64); let section = object.section_by_name(".zdebug_info").unwrap(); let uncompressed = section.uncompressed_data().unwrap(); assert_eq!(data, &*uncompressed); } #[test] fn note() { let endian = Endianness::Little; let mut object = write::Object::new(BinaryFormat::Elf, Architecture::X86_64, endian); // Add note section with align = 4. let mut buffer = Vec::new(); buffer .write_all(object::bytes_of(&elf::NoteHeader32 { n_namesz: U32::new(endian, 6), n_descsz: U32::new(endian, 11), n_type: U32::new(endian, 1), })) .unwrap(); buffer.write_all(b"name1\0\0\0").unwrap(); buffer.write_all(b"descriptor\0\0").unwrap(); buffer .write_all(object::bytes_of(&elf::NoteHeader32 { n_namesz: U32::new(endian, 6), n_descsz: U32::new(endian, 11), n_type: U32::new(endian, 2), })) .unwrap(); buffer.write_all(b"name2\0\0\0").unwrap(); buffer.write_all(b"descriptor\0\0").unwrap(); let section = object.add_section(Vec::new(), b".note4".to_vec(), SectionKind::Note); object.section_mut(section).set_data(buffer, 4); // Add note section with align = 8. let mut buffer = Vec::new(); buffer .write_all(object::bytes_of(&elf::NoteHeader32 { n_namesz: U32::new(endian, 6), n_descsz: U32::new(endian, 11), n_type: U32::new(endian, 1), })) .unwrap(); buffer.write_all(b"name1\0\0\0\0\0\0\0").unwrap(); buffer.write_all(b"descriptor\0\0\0\0\0\0").unwrap(); buffer .write_all(object::bytes_of(&elf::NoteHeader32 { n_namesz: U32::new(endian, 4), n_descsz: U32::new(endian, 11), n_type: U32::new(endian, 2), })) .unwrap(); buffer.write_all(b"abc\0").unwrap(); buffer.write_all(b"descriptor\0\0\0\0\0\0").unwrap(); let section = object.add_section(Vec::new(), b".note8".to_vec(), SectionKind::Note); object.section_mut(section).set_data(buffer, 8); let bytes = &*object.write().unwrap(); //std::fs::write(&"note.o", &bytes).unwrap(); let header = elf::FileHeader64::parse(bytes).unwrap(); let endian: LittleEndian = header.endian().unwrap(); let sections = header.sections(endian, bytes).unwrap(); let section = sections.section(SectionIndex(1)).unwrap(); assert_eq!(sections.section_name(endian, section).unwrap(), b".note4"); assert_eq!(section.sh_addralign(endian), 4); let mut notes = section.notes(endian, bytes).unwrap().unwrap(); let note = notes.next().unwrap().unwrap(); assert_eq!(note.name(), b"name1"); assert_eq!(note.desc(), b"descriptor\0"); assert_eq!(note.n_type(endian), 1); let note = notes.next().unwrap().unwrap(); assert_eq!(note.name(), b"name2"); assert_eq!(note.desc(), b"descriptor\0"); assert_eq!(note.n_type(endian), 2); assert!(notes.next().unwrap().is_none()); let section = sections.section(SectionIndex(2)).unwrap(); assert_eq!(sections.section_name(endian, section).unwrap(), b".note8"); assert_eq!(section.sh_addralign(endian), 8); let mut notes = section.notes(endian, bytes).unwrap().unwrap(); let note = notes.next().unwrap().unwrap(); assert_eq!(note.name(), b"name1"); assert_eq!(note.desc(), b"descriptor\0"); assert_eq!(note.n_type(endian), 1); let note = notes.next().unwrap().unwrap(); assert_eq!(note.name(), b"abc"); assert_eq!(note.desc(), b"descriptor\0"); assert_eq!(note.n_type(endian), 2); assert!(notes.next().unwrap().is_none()); } #[test] fn gnu_property() { gnu_property_inner::>(Architecture::I386); gnu_property_inner::>(Architecture::X86_64); } fn gnu_property_inner>(architecture: Architecture) { let endian = Endianness::Little; let mut object = write::Object::new(BinaryFormat::Elf, architecture, endian); object.add_elf_gnu_property_u32( elf::GNU_PROPERTY_X86_FEATURE_1_AND, elf::GNU_PROPERTY_X86_FEATURE_1_IBT | elf::GNU_PROPERTY_X86_FEATURE_1_SHSTK, ); let bytes = &*object.write().unwrap(); //std::fs::write(&"note.o", &bytes).unwrap(); let header = Elf::parse(bytes).unwrap(); assert_eq!(header.endian().unwrap(), endian); let sections = header.sections(endian, bytes).unwrap(); let section = sections.section(SectionIndex(1)).unwrap(); assert_eq!( sections.section_name(endian, section).unwrap(), b".note.gnu.property" ); assert_eq!(section.sh_flags(endian).into(), u64::from(elf::SHF_ALLOC)); let mut notes = section.notes(endian, bytes).unwrap().unwrap(); let note = notes.next().unwrap().unwrap(); let mut props = note.gnu_properties(endian).unwrap(); let prop = props.next().unwrap().unwrap(); assert_eq!(prop.pr_type(), elf::GNU_PROPERTY_X86_FEATURE_1_AND); assert_eq!( prop.data_u32(endian).unwrap(), elf::GNU_PROPERTY_X86_FEATURE_1_IBT | elf::GNU_PROPERTY_X86_FEATURE_1_SHSTK ); assert!(props.next().unwrap().is_none()); assert!(notes.next().unwrap().is_none()); } object-0.36.5/tests/round_trip/macho.rs000064400000000000000000000047371046102023000161640ustar 00000000000000use object::read::macho::MachHeader; use object::read::{Object, ObjectSection}; use object::{macho, read, write, Architecture, BinaryFormat, Endianness}; // Test that segment size is valid when the first section needs alignment. #[test] fn issue_286_segment_file_size() { let mut object = write::Object::new( BinaryFormat::MachO, Architecture::X86_64, Endianness::Little, ); let text = object.section_id(write::StandardSection::Text); object.append_section_data(text, &[1; 30], 0x1000); let bytes = &*object.write().unwrap(); let header = macho::MachHeader64::parse(bytes, 0).unwrap(); let endian: Endianness = header.endian().unwrap(); let mut commands = header.load_commands(endian, bytes, 0).unwrap(); let command = commands.next().unwrap().unwrap(); let (segment, _) = command.segment_64().unwrap().unwrap(); assert_eq!(segment.vmsize.get(endian), 30); assert_eq!(segment.filesize.get(endian), 30); } // We were emitting section file alignment padding that didn't match the address alignment padding. #[test] fn issue_552_section_file_alignment() { let mut object = write::Object::new( BinaryFormat::MachO, Architecture::X86_64, Endianness::Little, ); // The starting file offset is not a multiple of 32 (checked later). // Length of 32 ensures that the file offset of the end of this section is still not a // multiple of 32. let section = object.add_section(vec![], vec![], object::SectionKind::ReadOnlyDataWithRel); object.append_section_data(section, &[0u8; 32], 1); // Address is already aligned correctly, so there must not any padding, // even though file offset is not aligned. let section = object.add_section(vec![], vec![], object::SectionKind::ReadOnlyData); object.append_section_data(section, &[0u8; 1], 32); let bytes = &*object.write().unwrap(); //std::fs::write(&"align.o", &bytes).unwrap(); let object = read::File::parse(bytes).unwrap(); let mut sections = object.sections(); let section = sections.next().unwrap(); let offset = section.file_range().unwrap().0; // Check file offset is not aligned to 32. assert_ne!(offset % 32, 0); assert_eq!(section.address(), 0); assert_eq!(section.size(), 32); let section = sections.next().unwrap(); // Check there is no padding. assert_eq!(section.file_range(), Some((offset + 32, 1))); assert_eq!(section.address(), 32); assert_eq!(section.size(), 1); } object-0.36.5/tests/round_trip/mod.rs000064400000000000000000000576441046102023000156610ustar 00000000000000#![cfg(all(feature = "read", feature = "write"))] use object::read::{Object, ObjectSection, ObjectSymbol}; use object::{read, write, SectionIndex, SubArchitecture}; use object::{ Architecture, BinaryFormat, Endianness, RelocationEncoding, RelocationFlags, RelocationKind, SectionKind, SymbolFlags, SymbolKind, SymbolScope, SymbolSection, }; mod bss; mod coff; mod comdat; mod common; mod elf; mod macho; mod section_flags; mod tls; #[test] fn coff_any() { for (arch, sub_arch) in [ (Architecture::Aarch64, None), (Architecture::Aarch64, Some(SubArchitecture::Arm64EC)), (Architecture::Arm, None), (Architecture::I386, None), (Architecture::X86_64, None), ] .iter() .copied() { let mut object = write::Object::new(BinaryFormat::Coff, arch, Endianness::Little); object.set_sub_architecture(sub_arch); object.add_file_symbol(b"file.c".to_vec()); let text = object.section_id(write::StandardSection::Text); object.append_section_data(text, &[1; 30], 4); let func1_offset = object.append_section_data(text, &[1; 30], 4); assert_eq!(func1_offset, 32); let func1_symbol = object.add_symbol(write::Symbol { name: b"func1".to_vec(), value: func1_offset, size: 32, kind: SymbolKind::Text, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Section(text), flags: SymbolFlags::None, }); let func2_offset = object.append_section_data(text, &[1; 30], 4); assert_eq!(func2_offset, 64); object.add_symbol(write::Symbol { name: b"func2_long".to_vec(), value: func2_offset, size: 32, kind: SymbolKind::Text, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Section(text), flags: SymbolFlags::None, }); object .add_relocation( text, write::Relocation { offset: 8, symbol: func1_symbol, addend: 0, flags: RelocationFlags::Generic { kind: RelocationKind::Absolute, encoding: RelocationEncoding::Generic, size: arch.address_size().unwrap().bytes() * 8, }, }, ) .unwrap(); let bytes = object.write().unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::Coff); assert_eq!(object.architecture(), arch); assert_eq!(object.sub_architecture(), sub_arch); assert_eq!(object.endianness(), Endianness::Little); let mut sections = object.sections(); let text = sections.next().unwrap(); println!("{:?}", text); let text_index = text.index(); assert_eq!(text.name(), Ok(".text")); assert_eq!(text.kind(), SectionKind::Text); assert_eq!(text.address(), 0); assert_eq!(text.size(), 94); assert_eq!(&text.data().unwrap()[..30], &[1; 30]); assert_eq!(&text.data().unwrap()[32..62], &[1; 30]); let mut symbols = object.symbols(); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok("file.c")); assert_eq!(symbol.address(), 0); assert_eq!(symbol.kind(), SymbolKind::File); assert_eq!(symbol.section(), SymbolSection::None); assert_eq!(symbol.scope(), SymbolScope::Compilation); assert!(!symbol.is_weak()); let decorated_name = |name: &str| { if arch == Architecture::I386 { format!("_{name}") } else { name.to_owned() } }; let symbol = symbols.next().unwrap(); println!("{:?}", symbol); let func1_symbol = symbol.index(); assert_eq!(symbol.name(), Ok(decorated_name("func1").as_str())); assert_eq!(symbol.address(), func1_offset); assert_eq!(symbol.kind(), SymbolKind::Text); assert_eq!(symbol.section_index(), Some(text_index)); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok(decorated_name("func2_long").as_str())); assert_eq!(symbol.address(), func2_offset); assert_eq!(symbol.kind(), SymbolKind::Text); assert_eq!(symbol.section_index(), Some(text_index)); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); let mut relocations = text.relocations(); let (offset, relocation) = relocations.next().unwrap(); println!("{:?}", relocation); assert_eq!(offset, 8); assert_eq!(relocation.kind(), RelocationKind::Absolute); assert_eq!(relocation.encoding(), RelocationEncoding::Generic); assert_eq!(relocation.size(), arch.address_size().unwrap().bytes() * 8); assert_eq!( relocation.target(), read::RelocationTarget::Symbol(func1_symbol) ); assert_eq!(relocation.addend(), 0); let map = object.symbol_map(); let symbol = map.get(func1_offset + 1).unwrap(); assert_eq!(symbol.address(), func1_offset); assert_eq!(symbol.name(), decorated_name("func1")); assert_eq!(map.get(func1_offset - 1), None); } } #[test] fn elf_x86_64() { let mut object = write::Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); object.add_file_symbol(b"file.c".to_vec()); let text = object.section_id(write::StandardSection::Text); object.append_section_data(text, &[1; 30], 4); let func1_offset = object.append_section_data(text, &[1; 30], 4); assert_eq!(func1_offset, 32); let func1_symbol = object.add_symbol(write::Symbol { name: b"func1".to_vec(), value: func1_offset, size: 32, kind: SymbolKind::Text, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Section(text), flags: SymbolFlags::None, }); object .add_relocation( text, write::Relocation { offset: 8, symbol: func1_symbol, addend: 0, flags: RelocationFlags::Generic { kind: RelocationKind::Absolute, encoding: RelocationEncoding::Generic, size: 64, }, }, ) .unwrap(); let bytes = object.write().unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::Elf); assert_eq!(object.architecture(), Architecture::X86_64); assert_eq!(object.endianness(), Endianness::Little); let mut sections = object.sections(); let text = sections.next().unwrap(); println!("{:?}", text); let text_index = text.index(); assert_eq!(text.name(), Ok(".text")); assert_eq!(text.kind(), SectionKind::Text); assert_eq!(text.address(), 0); assert_eq!(text.size(), 62); assert_eq!(&text.data().unwrap()[..30], &[1; 30]); assert_eq!(&text.data().unwrap()[32..62], &[1; 30]); let mut symbols = object.symbols(); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok("file.c")); assert_eq!(symbol.address(), 0); assert_eq!(symbol.kind(), SymbolKind::File); assert_eq!(symbol.section(), SymbolSection::None); assert_eq!(symbol.scope(), SymbolScope::Compilation); assert!(!symbol.is_weak()); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); let func1_symbol = symbol.index(); assert_eq!(symbol.name(), Ok("func1")); assert_eq!(symbol.address(), func1_offset); assert_eq!(symbol.kind(), SymbolKind::Text); assert_eq!(symbol.section_index(), Some(text_index)); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); let mut relocations = text.relocations(); let (offset, relocation) = relocations.next().unwrap(); println!("{:?}", relocation); assert_eq!(offset, 8); assert_eq!(relocation.kind(), RelocationKind::Absolute); assert_eq!(relocation.encoding(), RelocationEncoding::Generic); assert_eq!(relocation.size(), 64); assert_eq!( relocation.target(), read::RelocationTarget::Symbol(func1_symbol) ); assert_eq!(relocation.addend(), 0); let map = object.symbol_map(); let symbol = map.get(func1_offset + 1).unwrap(); assert_eq!(symbol.address(), func1_offset); assert_eq!(symbol.name(), "func1"); assert_eq!(map.get(func1_offset - 1), None); } #[test] fn elf_any() { for (arch, endian) in [ (Architecture::Aarch64, Endianness::Little), (Architecture::Aarch64_Ilp32, Endianness::Little), (Architecture::Arm, Endianness::Little), (Architecture::Avr, Endianness::Little), (Architecture::Bpf, Endianness::Little), (Architecture::Csky, Endianness::Little), (Architecture::E2K32, Endianness::Little), (Architecture::E2K64, Endianness::Little), (Architecture::I386, Endianness::Little), (Architecture::X86_64, Endianness::Little), (Architecture::X86_64_X32, Endianness::Little), (Architecture::Hexagon, Endianness::Little), (Architecture::LoongArch64, Endianness::Little), (Architecture::Mips, Endianness::Little), (Architecture::Mips64, Endianness::Little), (Architecture::Msp430, Endianness::Little), (Architecture::PowerPc, Endianness::Big), (Architecture::PowerPc64, Endianness::Big), (Architecture::Riscv32, Endianness::Little), (Architecture::Riscv64, Endianness::Little), (Architecture::S390x, Endianness::Big), (Architecture::Sbf, Endianness::Little), (Architecture::Sparc, Endianness::Big), (Architecture::Sparc32Plus, Endianness::Big), (Architecture::Sparc64, Endianness::Big), (Architecture::Xtensa, Endianness::Little), ] .iter() .copied() { let mut object = write::Object::new(BinaryFormat::Elf, arch, endian); let section = object.section_id(write::StandardSection::Data); object.append_section_data(section, &[1; 30], 4); let symbol = object.section_symbol(section); object .add_relocation( section, write::Relocation { offset: 8, symbol, addend: 0, flags: RelocationFlags::Generic { kind: RelocationKind::Absolute, encoding: RelocationEncoding::Generic, size: 32, }, }, ) .unwrap(); if arch.address_size().unwrap().bytes() >= 8 { object .add_relocation( section, write::Relocation { offset: 16, symbol, addend: 0, flags: RelocationFlags::Generic { kind: RelocationKind::Absolute, encoding: RelocationEncoding::Generic, size: 64, }, }, ) .unwrap(); } let bytes = object.write().unwrap(); let object = read::File::parse(&*bytes).unwrap(); println!("{:?}", object.architecture()); assert_eq!(object.format(), BinaryFormat::Elf); assert_eq!(object.architecture(), arch); assert_eq!(object.endianness(), endian); let mut sections = object.sections(); let data = sections.next().unwrap(); println!("{:?}", data); assert_eq!(data.name(), Ok(".data")); assert_eq!(data.kind(), SectionKind::Data); let mut relocations = data.relocations(); let (offset, relocation) = relocations.next().unwrap(); println!("{:?}", relocation); assert_eq!(offset, 8); assert_eq!(relocation.kind(), RelocationKind::Absolute); assert_eq!(relocation.encoding(), RelocationEncoding::Generic); assert_eq!(relocation.size(), 32); assert_eq!(relocation.addend(), 0); if arch.address_size().unwrap().bytes() >= 8 { let (offset, relocation) = relocations.next().unwrap(); println!("{:?}", relocation); assert_eq!(offset, 16); assert_eq!(relocation.kind(), RelocationKind::Absolute); assert_eq!(relocation.encoding(), RelocationEncoding::Generic); assert_eq!(relocation.size(), 64); assert_eq!(relocation.addend(), 0); } } } #[test] fn macho_x86_64() { let mut object = write::Object::new( BinaryFormat::MachO, Architecture::X86_64, Endianness::Little, ); object.add_file_symbol(b"file.c".to_vec()); let text = object.section_id(write::StandardSection::Text); object.append_section_data(text, &[1; 30], 4); let func1_offset = object.append_section_data(text, &[1; 30], 4); assert_eq!(func1_offset, 32); let func1_symbol = object.add_symbol(write::Symbol { name: b"func1".to_vec(), value: func1_offset, size: 32, kind: SymbolKind::Text, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Section(text), flags: SymbolFlags::None, }); object .add_relocation( text, write::Relocation { offset: 8, symbol: func1_symbol, addend: 0, flags: RelocationFlags::Generic { kind: RelocationKind::Absolute, encoding: RelocationEncoding::Generic, size: 64, }, }, ) .unwrap(); object .add_relocation( text, write::Relocation { offset: 16, symbol: func1_symbol, addend: -4, flags: RelocationFlags::Generic { kind: RelocationKind::Relative, encoding: RelocationEncoding::Generic, size: 32, }, }, ) .unwrap(); let bytes = object.write().unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::MachO); assert_eq!(object.architecture(), Architecture::X86_64); assert_eq!(object.endianness(), Endianness::Little); let mut sections = object.sections(); let text = sections.next().unwrap(); println!("{:?}", text); let text_index = text.index(); assert_eq!(text.name(), Ok("__text")); assert_eq!(text.segment_name(), Ok(Some("__TEXT"))); assert_eq!(text.kind(), SectionKind::Text); assert_eq!(text.address(), 0); assert_eq!(text.size(), 62); assert_eq!(&text.data().unwrap()[..30], &[1; 30]); assert_eq!(&text.data().unwrap()[32..62], &[1; 30]); let mut symbols = object.symbols(); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); let func1_symbol = symbol.index(); assert_eq!(symbol.name(), Ok("_func1")); assert_eq!(symbol.address(), func1_offset); assert_eq!(symbol.kind(), SymbolKind::Text); assert_eq!(symbol.section_index(), Some(text_index)); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); let mut relocations = text.relocations(); let (offset, relocation) = relocations.next().unwrap(); println!("{:?}", relocation); assert_eq!(offset, 16); assert_eq!(relocation.kind(), RelocationKind::Relative); assert_eq!(relocation.encoding(), RelocationEncoding::X86RipRelative); assert_eq!(relocation.size(), 32); assert_eq!( relocation.target(), read::RelocationTarget::Symbol(func1_symbol) ); assert_eq!(relocation.addend(), -4); let (offset, relocation) = relocations.next().unwrap(); println!("{:?}", relocation); assert_eq!(offset, 8); assert_eq!(relocation.kind(), RelocationKind::Absolute); assert_eq!(relocation.encoding(), RelocationEncoding::Generic); assert_eq!(relocation.size(), 64); assert_eq!( relocation.target(), read::RelocationTarget::Symbol(func1_symbol) ); assert_eq!(relocation.addend(), 0); let map = object.symbol_map(); let symbol = map.get(func1_offset + 1).unwrap(); assert_eq!(symbol.address(), func1_offset); assert_eq!(symbol.name(), "_func1"); assert_eq!(map.get(func1_offset - 1), None); } #[test] fn macho_any() { for (arch, subarch, endian) in [ (Architecture::Aarch64, None, Endianness::Little), ( Architecture::Aarch64, Some(SubArchitecture::Arm64E), Endianness::Little, ), (Architecture::Aarch64_Ilp32, None, Endianness::Little), /* TODO: (Architecture::Arm, None, Endianness::Little), */ (Architecture::I386, None, Endianness::Little), (Architecture::X86_64, None, Endianness::Little), /* TODO: (Architecture::PowerPc, None, Endianness::Big), (Architecture::PowerPc64, None, Endianness::Big), */ ] .iter() .copied() { let mut object = write::Object::new(BinaryFormat::MachO, arch, endian); object.set_sub_architecture(subarch); let section = object.section_id(write::StandardSection::Data); object.append_section_data(section, &[1; 30], 4); let symbol = object.section_symbol(section); object .add_relocation( section, write::Relocation { offset: 8, symbol, addend: 0, flags: RelocationFlags::Generic { kind: RelocationKind::Absolute, encoding: RelocationEncoding::Generic, size: 32, }, }, ) .unwrap(); if arch.address_size().unwrap().bytes() >= 8 { object .add_relocation( section, write::Relocation { offset: 16, symbol, addend: 0, flags: RelocationFlags::Generic { kind: RelocationKind::Absolute, encoding: RelocationEncoding::Generic, size: 64, }, }, ) .unwrap(); } let bytes = object.write().unwrap(); let object = read::File::parse(&*bytes).unwrap(); println!("{:?}", object.architecture()); assert_eq!(object.format(), BinaryFormat::MachO); assert_eq!(object.architecture(), arch); assert_eq!(object.sub_architecture(), subarch); assert_eq!(object.endianness(), endian); let mut sections = object.sections(); let data = sections.next().unwrap(); println!("{:?}", data); assert_eq!(data.segment_name(), Ok(Some("__DATA"))); assert_eq!(data.name(), Ok("__data")); assert_eq!(data.kind(), SectionKind::Data); let mut relocations = data.relocations(); if arch.address_size().unwrap().bytes() >= 8 { let (offset, relocation) = relocations.next().unwrap(); println!("{:?}", relocation); assert_eq!(offset, 16); assert_eq!(relocation.kind(), RelocationKind::Absolute); assert_eq!(relocation.encoding(), RelocationEncoding::Generic); assert_eq!(relocation.size(), 64); assert_eq!(relocation.addend(), 0); } let (offset, relocation) = relocations.next().unwrap(); println!("{:?}", relocation); assert_eq!(offset, 8); assert_eq!(relocation.kind(), RelocationKind::Absolute); assert_eq!(relocation.encoding(), RelocationEncoding::Generic); assert_eq!(relocation.size(), 32); assert_eq!(relocation.addend(), 0); } } #[cfg(feature = "xcoff")] #[test] fn xcoff_powerpc() { for arch in [Architecture::PowerPc, Architecture::PowerPc64] { let mut object = write::Object::new(BinaryFormat::Xcoff, arch, Endianness::Big); object.add_file_symbol(b"file.c".to_vec()); let text = object.section_id(write::StandardSection::Text); object.append_section_data(text, &[1; 30], 4); let func1_offset = object.append_section_data(text, &[1; 30], 4); assert_eq!(func1_offset, 32); let func1_symbol = object.add_symbol(write::Symbol { name: b"func1".to_vec(), value: func1_offset, size: 32, kind: SymbolKind::Text, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Section(text), flags: SymbolFlags::None, }); object .add_relocation( text, write::Relocation { offset: 8, symbol: func1_symbol, addend: 0, flags: RelocationFlags::Generic { kind: RelocationKind::Absolute, encoding: RelocationEncoding::Generic, size: 64, }, }, ) .unwrap(); let bytes = object.write().unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::Xcoff); assert_eq!(object.architecture(), arch); assert_eq!(object.endianness(), Endianness::Big); let mut sections = object.sections(); let text = sections.next().unwrap(); println!("{:?}", text); let text_index = text.index().0; assert_eq!(text.name(), Ok(".text")); assert_eq!(text.kind(), SectionKind::Text); assert_eq!(text.address(), 0); assert_eq!(text.size(), 62); assert_eq!(&text.data().unwrap()[..30], &[1; 30]); assert_eq!(&text.data().unwrap()[32..62], &[1; 30]); let mut symbols = object.symbols(); let mut symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok("file.c")); assert_eq!(symbol.address(), 0); assert_eq!(symbol.kind(), SymbolKind::File); assert_eq!(symbol.section_index(), None); assert_eq!(symbol.scope(), SymbolScope::Compilation); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); symbol = symbols.next().unwrap(); println!("{:?}", symbol); let func1_symbol = symbol.index(); assert_eq!(symbol.name(), Ok("func1")); assert_eq!(symbol.address(), func1_offset); assert_eq!(symbol.kind(), SymbolKind::Text); assert_eq!(symbol.section_index(), Some(SectionIndex(text_index))); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); let mut relocations = text.relocations(); let (offset, relocation) = relocations.next().unwrap(); println!("{:?}", relocation); assert_eq!(offset, 8); assert_eq!(relocation.kind(), RelocationKind::Absolute); assert_eq!(relocation.encoding(), RelocationEncoding::Generic); assert_eq!(relocation.size(), 64); assert_eq!( relocation.target(), read::RelocationTarget::Symbol(func1_symbol) ); assert_eq!(relocation.addend(), 0); } } object-0.36.5/tests/round_trip/section_flags.rs000064400000000000000000000054161046102023000177100ustar 00000000000000#![cfg(all(feature = "read", feature = "write"))] use object::read::{Object, ObjectSection}; use object::{read, write}; use object::{Architecture, BinaryFormat, Endianness, SectionFlags, SectionKind}; #[test] fn coff_x86_64_section_flags() { let mut object = write::Object::new(BinaryFormat::Coff, Architecture::X86_64, Endianness::Little); let section = object.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text); object.section_mut(section).flags = SectionFlags::Coff { characteristics: object::pe::IMAGE_SCN_MEM_WRITE, }; let bytes = object.write().unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::Coff); assert_eq!(object.architecture(), Architecture::X86_64); let mut sections = object.sections(); let section = sections.next().unwrap(); assert_eq!(section.name(), Ok(".text")); assert_eq!( section.flags(), SectionFlags::Coff { characteristics: object::pe::IMAGE_SCN_MEM_WRITE | object::pe::IMAGE_SCN_ALIGN_1BYTES, } ); } #[test] fn elf_x86_64_section_flags() { let mut object = write::Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); let section = object.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text); object.section_mut(section).flags = SectionFlags::Elf { sh_flags: object::elf::SHF_WRITE.into(), }; let bytes = object.write().unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::Elf); assert_eq!(object.architecture(), Architecture::X86_64); let mut sections = object.sections(); let section = sections.next().unwrap(); assert_eq!(section.name(), Ok(".text")); assert_eq!( section.flags(), SectionFlags::Elf { sh_flags: object::elf::SHF_WRITE.into(), } ); } #[test] fn macho_x86_64_section_flags() { let mut object = write::Object::new( BinaryFormat::MachO, Architecture::X86_64, Endianness::Little, ); let section = object.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text); object.section_mut(section).flags = SectionFlags::MachO { flags: object::macho::S_ATTR_SELF_MODIFYING_CODE, }; let bytes = object.write().unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::MachO); assert_eq!(object.architecture(), Architecture::X86_64); let mut sections = object.sections(); let section = sections.next().unwrap(); assert_eq!(section.name(), Ok(".text")); assert_eq!( section.flags(), SectionFlags::MachO { flags: object::macho::S_ATTR_SELF_MODIFYING_CODE, } ); } object-0.36.5/tests/round_trip/tls.rs000064400000000000000000000251741046102023000156750ustar 00000000000000#![cfg(all(feature = "read", feature = "write"))] use object::read::{Object, ObjectSection, ObjectSymbol}; use object::{read, write}; use object::{ Architecture, BinaryFormat, Endianness, RelocationEncoding, RelocationKind, SectionKind, SymbolFlags, SymbolKind, SymbolScope, }; #[test] fn coff_x86_64_tls() { let mut object = write::Object::new(BinaryFormat::Coff, Architecture::X86_64, Endianness::Little); let section = object.section_id(write::StandardSection::Tls); let symbol = object.add_symbol(write::Symbol { name: b"tls1".to_vec(), value: 0, size: 0, kind: SymbolKind::Tls, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Undefined, flags: SymbolFlags::None, }); object.add_symbol_data(symbol, section, &[1; 30], 4); let bytes = object.write().unwrap(); //std::fs::write(&"tls.o", &bytes).unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::Coff); assert_eq!(object.architecture(), Architecture::X86_64); let mut sections = object.sections(); let section = sections.next().unwrap(); println!("{:?}", section); let tls_index = section.index(); assert_eq!(section.name(), Ok(".tls$")); assert_eq!(section.kind(), SectionKind::Data); assert_eq!(section.size(), 30); assert_eq!(section.data().unwrap(), &[1; 30]); let mut symbols = object.symbols(); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok("tls1")); assert_eq!(symbol.kind(), SymbolKind::Data); assert_eq!(symbol.section_index(), Some(tls_index)); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); } #[test] fn elf_x86_64_tls() { let mut object = write::Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little); let section = object.section_id(write::StandardSection::Tls); let symbol = object.add_symbol(write::Symbol { name: b"tls1".to_vec(), value: 0, size: 0, kind: SymbolKind::Tls, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Undefined, flags: SymbolFlags::None, }); object.add_symbol_data(symbol, section, &[1; 30], 4); let section = object.section_id(write::StandardSection::UninitializedTls); let symbol = object.add_symbol(write::Symbol { name: b"tls2".to_vec(), value: 0, size: 0, kind: SymbolKind::Tls, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Undefined, flags: SymbolFlags::None, }); object.add_symbol_bss(symbol, section, 31, 4); let bytes = object.write().unwrap(); //std::fs::write(&"tls.o", &bytes).unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::Elf); assert_eq!(object.architecture(), Architecture::X86_64); let mut sections = object.sections(); let section = sections.next().unwrap(); println!("{:?}", section); let tdata_index = section.index(); assert_eq!(section.name(), Ok(".tdata")); assert_eq!(section.kind(), SectionKind::Tls); assert_eq!(section.size(), 30); assert_eq!(section.data().unwrap(), &[1; 30]); let section = sections.next().unwrap(); println!("{:?}", section); let tbss_index = section.index(); assert_eq!(section.name(), Ok(".tbss")); assert_eq!(section.kind(), SectionKind::UninitializedTls); assert_eq!(section.size(), 31); assert_eq!(section.data().unwrap(), &[]); let mut symbols = object.symbols(); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok("tls1")); assert_eq!(symbol.kind(), SymbolKind::Tls); assert_eq!(symbol.section_index(), Some(tdata_index)); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); assert_eq!(symbol.size(), 30); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok("tls2")); assert_eq!(symbol.kind(), SymbolKind::Tls); assert_eq!(symbol.section_index(), Some(tbss_index)); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); assert_eq!(symbol.size(), 31); } #[test] fn macho_x86_64_tls() { let mut object = write::Object::new( BinaryFormat::MachO, Architecture::X86_64, Endianness::Little, ); let section = object.section_id(write::StandardSection::Tls); let symbol = object.add_symbol(write::Symbol { name: b"tls1".to_vec(), value: 0, size: 0, kind: SymbolKind::Tls, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Undefined, flags: SymbolFlags::None, }); object.add_symbol_data(symbol, section, &[1; 30], 4); let section = object.section_id(write::StandardSection::UninitializedTls); let symbol = object.add_symbol(write::Symbol { name: b"tls2".to_vec(), value: 0, size: 0, kind: SymbolKind::Tls, scope: SymbolScope::Linkage, weak: false, section: write::SymbolSection::Undefined, flags: SymbolFlags::None, }); object.add_symbol_bss(symbol, section, 31, 4); let bytes = object.write().unwrap(); //std::fs::write(&"tls.o", &bytes).unwrap(); let object = read::File::parse(&*bytes).unwrap(); assert_eq!(object.format(), BinaryFormat::MachO); assert_eq!(object.architecture(), Architecture::X86_64); let mut sections = object.sections(); let thread_data = sections.next().unwrap(); println!("{:?}", thread_data); let thread_data_index = thread_data.index(); assert_eq!(thread_data.name(), Ok("__thread_data")); assert_eq!(thread_data.segment_name(), Ok(Some("__DATA"))); assert_eq!(thread_data.kind(), SectionKind::Tls); assert_eq!(thread_data.size(), 30); assert_eq!(thread_data.data().unwrap(), &[1; 30]); let thread_vars = sections.next().unwrap(); println!("{:?}", thread_vars); let thread_vars_index = thread_vars.index(); assert_eq!(thread_vars.name(), Ok("__thread_vars")); assert_eq!(thread_vars.segment_name(), Ok(Some("__DATA"))); assert_eq!(thread_vars.kind(), SectionKind::TlsVariables); assert_eq!(thread_vars.size(), 2 * 3 * 8); assert_eq!(thread_vars.data().unwrap(), &[0; 48]); let thread_bss = sections.next().unwrap(); println!("{:?}", thread_bss); let thread_bss_index = thread_bss.index(); assert_eq!(thread_bss.name(), Ok("__thread_bss")); assert_eq!(thread_bss.segment_name(), Ok(Some("__DATA"))); assert_eq!(thread_bss.kind(), SectionKind::UninitializedTls); assert_eq!(thread_bss.size(), 31); assert_eq!(thread_bss.data(), Ok(&[][..])); let mut symbols = object.symbols(); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); let tls1_init_symbol = symbol.index(); assert_eq!(symbol.name(), Ok("_tls1$tlv$init")); assert_eq!(symbol.kind(), SymbolKind::Tls); assert_eq!(symbol.section_index(), Some(thread_data_index)); assert_eq!(symbol.scope(), SymbolScope::Compilation); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); let tls2_init_symbol = symbol.index(); assert_eq!(symbol.name(), Ok("_tls2$tlv$init")); assert_eq!(symbol.kind(), SymbolKind::Tls); assert_eq!(symbol.section_index(), Some(thread_bss_index)); assert_eq!(symbol.scope(), SymbolScope::Compilation); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok("_tls1")); assert_eq!(symbol.kind(), SymbolKind::Tls); assert_eq!(symbol.section_index(), Some(thread_vars_index)); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); assert_eq!(symbol.name(), Ok("_tls2")); assert_eq!(symbol.kind(), SymbolKind::Tls); assert_eq!(symbol.section_index(), Some(thread_vars_index)); assert_eq!(symbol.scope(), SymbolScope::Linkage); assert!(!symbol.is_weak()); assert!(!symbol.is_undefined()); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); let tlv_bootstrap_symbol = symbol.index(); assert_eq!(symbol.name(), Ok("__tlv_bootstrap")); assert_eq!(symbol.kind(), SymbolKind::Unknown); assert_eq!(symbol.section_index(), None); assert_eq!(symbol.scope(), SymbolScope::Unknown); assert!(!symbol.is_weak()); assert!(symbol.is_undefined()); let mut relocations = thread_vars.relocations(); let (offset, relocation) = relocations.next().unwrap(); println!("{:?}", relocation); assert_eq!(offset, 40); assert_eq!(relocation.kind(), RelocationKind::Absolute); assert_eq!(relocation.encoding(), RelocationEncoding::Generic); assert_eq!(relocation.size(), 64); assert_eq!( relocation.target(), read::RelocationTarget::Symbol(tls2_init_symbol) ); assert_eq!(relocation.addend(), 0); let (offset, relocation) = relocations.next().unwrap(); println!("{:?}", relocation); assert_eq!(offset, 24); assert_eq!(relocation.kind(), RelocationKind::Absolute); assert_eq!(relocation.encoding(), RelocationEncoding::Generic); assert_eq!(relocation.size(), 64); assert_eq!( relocation.target(), read::RelocationTarget::Symbol(tlv_bootstrap_symbol) ); assert_eq!(relocation.addend(), 0); let (offset, relocation) = relocations.next().unwrap(); println!("{:?}", relocation); assert_eq!(offset, 16); assert_eq!(relocation.kind(), RelocationKind::Absolute); assert_eq!(relocation.encoding(), RelocationEncoding::Generic); assert_eq!(relocation.size(), 64); assert_eq!( relocation.target(), read::RelocationTarget::Symbol(tls1_init_symbol) ); assert_eq!(relocation.addend(), 0); let (offset, relocation) = relocations.next().unwrap(); println!("{:?}", relocation); assert_eq!(offset, 0); assert_eq!(relocation.kind(), RelocationKind::Absolute); assert_eq!(relocation.encoding(), RelocationEncoding::Generic); assert_eq!(relocation.size(), 64); assert_eq!( relocation.target(), read::RelocationTarget::Symbol(tlv_bootstrap_symbol) ); assert_eq!(relocation.addend(), 0); }