diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/rust/object | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/object')
92 files changed, 46082 insertions, 0 deletions
diff --git a/third_party/rust/object/.cargo-checksum.json b/third_party/rust/object/.cargo-checksum.json new file mode 100644 index 0000000000..a8b6bddc54 --- /dev/null +++ b/third_party/rust/object/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"CHANGELOG.md":"19f059b47fe070336813f4561b7da9be7f8dc1968daf3b1565b1a55900673469","Cargo.toml":"8075e73eb31b58aacb542434e0f21851895a335efc6cc23724411c478716c213","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"0b74dfa0bcee5c420c6b7f67b4b2658f9ab8388c97b8e733975f2cecbdd668a6","README.md":"3772fdfd89e6321cc298413b8c0ba847bd125dfe64785e8faeaac05a87cbb2d3","clippy.toml":"e1e95839ba8e8bbf07f99ff49e2f151b9048c7981301a5480571842bbaf78ca2","src/archive.rs":"d6cead723242c26db2967b63385b79ed2008980a8c64b123a5eecffd7ed388fc","src/common.rs":"27a01eb0a1efcd10a988bc3922a2159e97fd133fbc07ff90faca4ff1722bd605","src/elf.rs":"ceb4f748b65ea092cf057db45193b0b2c7f9204a1e677848aaf0463f9dd3ac6a","src/endian.rs":"b4f63a85ccd3d5c11615baf5ba946c82046c221a1b8c74ee8c6fa4360782980c","src/lib.rs":"7f1261ebed84cd10169c1846a70dcafa7c8b25c06f32981335a9a676f45856a4","src/macho.rs":"e62808b0c84494b68e7e69b2f7fd9c0620b2ef61b6a2af3403de30284cf6b898","src/pe.rs":"8432e949a5bbf7ca28ba27f6a8ee926fe2e8632ffd88845714a6f64d6f7c0a40","src/pod.rs":"d2967732f0052e6cfa18a2dd62c57bc3b640a20eb9a6db9f39836000ceabb399","src/read/any.rs":"8ef08da67f083ee3f2758b5d64349d8032d5c63198d0c55815cd17054281a207","src/read/archive.rs":"479574cff125a74fc5512d75c1531da3bb006005fe544ffd2531a7d4f35a9bb4","src/read/coff/comdat.rs":"5204e6e44a33d1fe04ae551892d9dae2bd8ca4a0d9f1b46dd06a9e512b8ec6cc","src/read/coff/file.rs":"007c52c0a2d6f763a3c74e13f5cc15cf870a0701b87f01346ad550e52c5e5146","src/read/coff/import.rs":"1de0db515a0a6b1eee355d93a715f6d2ff152d1b815e1e859af0b0e5ded36efc","src/read/coff/mod.rs":"66bdcc772291cb53be378a66723e3c246e30df5aa4e4d4ce8e55bfeb156849a9","src/read/coff/relocation.rs":"57deb22ed0c9c31de0fe0bb483ded94459bcfd93b9f979191500d223a8517fe2","src/read/coff/section.rs":"de023dc46d4e1a37e77ab8e7080ff9fc9588633d790ae664e138d70e8fc8a39f","src/read/coff/symbol.rs":"e3b629112fe73e4627daa45093df1a1a2844ffe395d1c4de479f0f4c2bdac624","src/read/elf/attributes.rs":"1a5bc8dc081903d6b318761f50010ab08dee74b7f2d68ffbbd1aff7ed7b7c538","src/read/elf/comdat.rs":"253e00621d9a5249cfa20a2ebda76127113b42fc960235308333c9d35acdd628","src/read/elf/compression.rs":"097ff8bdc78d01a1532b11c1c0cae3b35905128c7d98b471de188d46da3ff970","src/read/elf/dynamic.rs":"8f59bd6d352f6810be6b6dc02c2f88229f15aa02a42f8fc09bcf3f284d4b1021","src/read/elf/file.rs":"860f6ed4e98e8672daef71065ea885f47d25593f6adeb7907545dadb9d398768","src/read/elf/hash.rs":"b330af7e2356512cfdf162986437c81a3b149a91e26bf82455a6976e2571a618","src/read/elf/mod.rs":"fceb322ac4e2cb182e0abd93f794622e58ae7c64e83c8703c9c888af5d93889d","src/read/elf/note.rs":"4b7e33582bff27918240d34d70f0099260dfd3122345c12ce10e6f7fcdf79e35","src/read/elf/relocation.rs":"7b95b7fbac280645f6d5900cc0158653bc8fb3adc9beeeae1b3f61eb77ada5d0","src/read/elf/section.rs":"c626a53afa196ba5b81a3d6278956f1d1ddf04ff2ac81f934425cfaef870aafd","src/read/elf/segment.rs":"ebcefba06c6f4a3e60403a1112c59f3390c39840aac011a8b040c77f8d29d655","src/read/elf/symbol.rs":"d50755b3acd34b9c77dd51f94d436265e79ed8f6f167b593e2a482543abde30b","src/read/elf/version.rs":"2c5f63e526cf26f83bdaaead4124949fc53b6198983e4681b6a3895794ad8754","src/read/macho/dyld_cache.rs":"c5247e9121a57fd411c86aea435b9c3d6d03d3cbb3fb7dfe6e75576013d71f14","src/read/macho/fat.rs":"d27a1052f2e47cd5b798a6359f33c3bfe7f7971b13259f6545118213ace7f5dd","src/read/macho/file.rs":"2531c245722202173ff762179af2c88af0108156457c848e7d756e348e72bc80","src/read/macho/load_command.rs":"d8c0ebc8f65dafa4fbb1f9a3c944fdeee96fddacfcc863650b94d9a62e8fe37a","src/read/macho/mod.rs":"23b353da3b7e076c68a067776e6a5b346a746116ac42c2c90bafd95ad54a90b3","src/read/macho/relocation.rs":"477d98b507550c29ceceba49308d14e1f086cb01a4ea9af691e995d1d29767a4","src/read/macho/section.rs":"9b21b3a02f509bb09983d116d7f0938cd61d5293e5e31907964611495b8a575e","src/read/macho/segment.rs":"0dee483eb9b6c731e69a9fe0bd9ef84b2a797a8203e5e870e15a6a2165326cc3","src/read/macho/symbol.rs":"3d3ad557c205d834ba305a9eaf3b97b2ed05f1906cd8430ad2fadabf81f14b9f","src/read/mod.rs":"b1224dc6654be52b8c2f6403503ffa662b576d05c0382146a386915d23c894dd","src/read/pe/data_directory.rs":"c08c095a4287c55bf7d7774bbade1d7610d8e82433b0de23af8c4c7ef23d75e3","src/read/pe/export.rs":"07ac5ec7b67d4a09037d8f11eb4426d96515687ee299df2a3d8cd4fd93eb2036","src/read/pe/file.rs":"507d800775adae687ca9ac7dca1a6f42463103af0731a5fe548d4745a9bee686","src/read/pe/import.rs":"ea20dfc0d462ba20e149bf9408f4ec1d0b202abf1f15536f6d091f0c0e756ac8","src/read/pe/mod.rs":"69832b7f4ccd93b59e08bafcbd0d3226c450d7801ad49ab554b38b660c8997fd","src/read/pe/relocation.rs":"0335c06b6d37df4939c8b88044313e88661ee45e5a57d2eec40143f2fe481838","src/read/pe/resource.rs":"0209eb96391bc367633b6d868505cb30947157702b0c85ef6677e5a1283d448f","src/read/pe/rich.rs":"ae9b2fc927bab2661e8d200a10128aebde37d26b50cb9069e9af9eb7bacee591","src/read/pe/section.rs":"bafde5a1584f6ce0456e32756e825c1b2b9dc0ec220ab29e668c2ff700600b87","src/read/read_cache.rs":"e9dbaa385435f5ef6ca5951c26ed1f6793ad3a8f3aee918257a5df6783d4b36d","src/read/read_ref.rs":"14966a1da9951633a7e73aedccfeadbbed4a977a8fb9d415d572250f6ebaf438","src/read/traits.rs":"8d094ba6ac06639bad448c70378d5154195d4ab36b13588b72105a869dfc053e","src/read/util.rs":"7ed8c5c88a52549734df67d2cbd3f0ea1a571728cae62152e57018f3140f9ebc","src/read/wasm.rs":"31c755ce17bba20ba287f53af7b7787bdbb5f8920333412fb11d81239102b423","src/read/xcoff/comdat.rs":"9a0a8a16682a28a54b51d28d382578e4b1e0212a34460eb93b50e8f97e4bf745","src/read/xcoff/file.rs":"17f751578d052cb8f74ee56a4e17b053b06e82e4efbe943907943bc561fb301e","src/read/xcoff/mod.rs":"d0179d3f95797464ca5919563454d1123ce8c35dfc5f40ecd6ca0d002a9824a8","src/read/xcoff/relocation.rs":"84993f477231cd1b8c79c385bc0e892640de89ddae268b845bc82c41999953b4","src/read/xcoff/section.rs":"c5ce72c214398125c7a4bc160fc5a7b27a6d92035dadcc84fd2852b73c75c123","src/read/xcoff/segment.rs":"627dabf3003aa1442bb4a2292cd68e1f572c3b95864a99e50a505b2894ffc804","src/read/xcoff/symbol.rs":"23be12b614937f8ea5de90d097fb8034f82bff798aa09636cd53a0670e39a984","src/write/coff.rs":"1dcb1c6c939af192591d44e135e6d139b82563d8bca07249e93fa3d434b1f6c7","src/write/elf/mod.rs":"1bb945edad539b4f19dda5d46c9b86fa4ea3721eedda77ca2595b5519c3e30f2","src/write/elf/object.rs":"2176f0f640eed5a05be77208301a10667533b495551662186e6c708bd02c9c18","src/write/elf/writer.rs":"84336230a24413a41e342735c153c3421c70707ec92c2df02e7d3536e5d41b55","src/write/macho.rs":"bd6afba576273a162f6d2e9658c8258a664c38d0169864310dffbf1fd7582a43","src/write/mod.rs":"c6c9a54ef807a3bee0c3074be86b0aed90872e1e703f39f2fa5c06bc9ed89767","src/write/pe.rs":"6c72185705a3e067c481f2b9f81c64a84e062e67781928e58fd1150314dad8f9","src/write/string.rs":"0033a6f5137b42988ac41dbaa2efb94a4d74d8b043c9a34c40125e8ee6912420","src/write/util.rs":"7a1083d305e9446767ce2d5f69be2c4c155495cf97e595f8fa53c4e153ccf186","src/write/xcoff.rs":"f192dd34fb442cd53a004e50508f6a787c9e9cd9089a15f63fdf6054eb6bd63f","src/xcoff.rs":"fbd50fc4b61ccfdf218185ea4eafe8cf9793e8d034e7ce243fb54ecae12af5ce","tests/integration.rs":"0fa704827e4da1be38dac2e3820d92f6b20c4d415803b04f67c3516020b1de97","tests/parse_self.rs":"81b44b2dd1de9a5d8c18d9bd8926156e39fb83931837afa8ca344da0d309aeee","tests/read/coff.rs":"d3ec2079f00237640d01cb66eb24c55c85d7a775bb94f9f5c9f77e21cb7a785d","tests/read/mod.rs":"7833826f169ac3be2b4f274e5fc8cf4a51742bd0010803ff0dc20ea5643a7e61","tests/round_trip/bss.rs":"849d69b063fd757fed02219dd81e9d13b82068a2025d2cc5cfd40cf557e31bda","tests/round_trip/coff.rs":"8a25aab7164a5c8aa7a21279f8bae1f4d5f68a8d09c29a4ecd0d0c14564851cc","tests/round_trip/comdat.rs":"a8f729e218fee21e90b9f39b5cfcb4f80bc3ce26d3a297323667e6eb14f882cc","tests/round_trip/common.rs":"ced08ff559ca4d343ceef54bb4c581a3405cd96d6a1628ba43b7aab82070800b","tests/round_trip/elf.rs":"d7351d888ccad246a646ab3bea1afc3d445adeb28c5d3c8f157f7cde3717281c","tests/round_trip/macho.rs":"8cf6297f1b9e31153b15f2e409e68b561f135a233d32b601a47f5fd4dfa014cc","tests/round_trip/mod.rs":"14db36fae698b75fedc4dc832465394350049f54b01b1215022a44ebe920f7e9","tests/round_trip/section_flags.rs":"0e17639e5f86d576f039a294c274ce8db2e2a8add31a2fffc33a6e93a6d2791e","tests/round_trip/tls.rs":"23a49a1036b9173ece82a3080745930e5925e745280ab38866c9d3c29f463e63"},"package":"77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe"}
\ No newline at end of file diff --git a/third_party/rust/object/CHANGELOG.md b/third_party/rust/object/CHANGELOG.md new file mode 100644 index 0000000000..451fcb1063 --- /dev/null +++ b/third_party/rust/object/CHANGELOG.md @@ -0,0 +1,679 @@ +# `object` Change Log + +-------------------------------------------------------------------------------- + +## 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) diff --git a/third_party/rust/object/Cargo.toml b/third_party/rust/object/Cargo.toml new file mode 100644 index 0000000000..385666a10b --- /dev/null +++ b/third_party/rust/object/Cargo.toml @@ -0,0 +1,163 @@ +# 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.60" +name = "object" +version = "0.32.0" +exclude = [ + "/.github", + "/testfiles", +] +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"] + +[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.14.0" +features = ["ahash"] +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.4.0" +optional = true + +[dependencies.wasmparser] +version = "0.110.0" +optional = true + +[features] +all = [ + "read", + "write", + "std", + "compression", + "wasm", +] +archive = [] +cargo-all = [] +coff = [] +compression = [ + "dep:flate2", + "dep:ruzstd", + "std", +] +default = [ + "read", + "compression", +] +doc = [ + "read_core", + "write_std", + "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 = [] diff --git a/third_party/rust/object/LICENSE-APACHE b/third_party/rust/object/LICENSE-APACHE new file mode 100644 index 0000000000..16fe87b06e --- /dev/null +++ b/third_party/rust/object/LICENSE-APACHE @@ -0,0 +1,201 @@ + 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. diff --git a/third_party/rust/object/LICENSE-MIT b/third_party/rust/object/LICENSE-MIT new file mode 100644 index 0000000000..8440c7e95f --- /dev/null +++ b/third_party/rust/object/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (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. diff --git a/third_party/rust/object/README.md b/third_party/rust/object/README.md new file mode 100644 index 0000000000..5b90fca306 --- /dev/null +++ b/third_party/rust/object/README.md @@ -0,0 +1,58 @@ +# `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: ELF, Mach-O, Windows PE/COFF, Wasm, XCOFF, and Unix archive. + +## Example for unified read API +```rust +use object::{Object, ObjectSection}; +use std::error::Error; +use std::fs; + +/// Reads a file and displays the content of the ".boot" section. +fn main() -> Result<(), Box<dyn Error>> { + let bin_data = fs::read("./multiboot2-binary.elf")?; + let obj_file = object::File::parse(&*bin_data)?; + if let Some(section) = obj_file.section_by_name(".boot") { + println!("{:#x?}", section.data()?); + } else { + eprintln!("section not available"); + } + 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.60.0 for the `read` feature and its dependencies. + * 1.65.0 for other features. + +## 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. diff --git a/third_party/rust/object/clippy.toml b/third_party/rust/object/clippy.toml new file mode 100644 index 0000000000..16caf02ee9 --- /dev/null +++ b/third_party/rust/object/clippy.toml @@ -0,0 +1 @@ +msrv = "1.60.0" diff --git a/third_party/rust/object/src/archive.rs b/third_party/rust/object/src/archive.rs new file mode 100644 index 0000000000..6271d07360 --- /dev/null +++ b/third_party/rust/object/src/archive.rs @@ -0,0 +1,91 @@ +//! 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"!<arch>\n"; + +/// File identification bytes at the beginning of AIX big archive. +pub const AIX_BIG_MAGIC: [u8; 8] = *b"<bigaf>\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"!<thin>\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,); diff --git a/third_party/rust/object/src/common.rs b/third_party/rust/object/src/common.rs new file mode 100644 index 0000000000..0e6af091ce --- /dev/null +++ b/third_party/rust/object/src/common.rs @@ -0,0 +1,501 @@ +/// 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, + I386, + X86_64, + #[allow(non_camel_case_types)] + X86_64_X32, + Hexagon, + LoongArch64, + Mips, + Mips64, + Msp430, + PowerPc, + PowerPc64, + Riscv32, + Riscv64, + S390x, + Sbf, + Sparc64, + Wasm32, + Wasm64, + Xtensa, +} + +impl Architecture { + /// The size of an address value for this architecture. + /// + /// Returns `None` for unknown architectures. + pub fn address_size(self) -> Option<AddressSize> { + 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::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::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, +} + +/// 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, + /// 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 a null placeholder. + Null, + /// The symbol is for executable code. + Text, + /// The symbol is for a data object. + Data, + /// The symbol is for a section. + Section, + /// The symbol is the name of a file. It precedes symbols within that file. + File, + /// The symbol is for 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 { + /// 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, + /// Some other ELF relocation. The value is dependent on the architecture. + Elf(u32), + /// Some other Mach-O relocation. The value is dependent on the architecture. + MachO { + /// The relocation type. + value: u8, + /// Whether the relocation is relative to the place. + relative: bool, + }, + /// Some other COFF relocation. The value is dependent on the architecture. + Coff(u16), + /// Some other XCOFF relocation. + Xcoff(u8), +} + +/// 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 { + /// 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, +} + +/// 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<Section, Symbol> { + /// 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<Section>, + }, + /// 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<Symbol>, + }, +} diff --git a/third_party/rust/object/src/elf.rs b/third_party/rust/object/src/elf.rs new file mode 100644 index 0000000000..da68ec2d40 --- /dev/null +++ b/third_party/rust/object/src/elf.rs @@ -0,0 +1,6156 @@ +//! 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<E: Endian> { + /// Magic number and other information. + pub e_ident: Ident, + /// Object file type. One of the `ET_*` constants. + pub e_type: U16<E>, + /// Architecture. One of the `EM_*` constants. + pub e_machine: U16<E>, + /// Object file version. Must be `EV_CURRENT`. + pub e_version: U32<E>, + /// Entry point virtual address. + pub e_entry: U32<E>, + /// Program header table file offset. + pub e_phoff: U32<E>, + /// Section header table file offset. + pub e_shoff: U32<E>, + /// Processor-specific flags. + /// + /// A combination of the `EF_*` constants. + pub e_flags: U32<E>, + /// Size in bytes of this header. + pub e_ehsize: U16<E>, + /// Program header table entry size. + pub e_phentsize: U16<E>, + /// 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<E>, + /// Section header table entry size. + pub e_shentsize: U16<E>, + /// 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<E>, + /// 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<E>, +} + +/// The header at the start of every 64-bit ELF file. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct FileHeader64<E: Endian> { + /// Magic number and other information. + pub e_ident: Ident, + /// Object file type. One of the `ET_*` constants. + pub e_type: U16<E>, + /// Architecture. One of the `EM_*` constants. + pub e_machine: U16<E>, + /// Object file version. Must be `EV_CURRENT`. + pub e_version: U32<E>, + /// Entry point virtual address. + pub e_entry: U64<E>, + /// Program header table file offset. + pub e_phoff: U64<E>, + /// Section header table file offset. + pub e_shoff: U64<E>, + /// Processor-specific flags. + /// + /// A combination of the `EF_*` constants. + pub e_flags: U32<E>, + /// Size in bytes of this header. + pub e_ehsize: U16<E>, + /// Program header table entry size. + pub e_phentsize: U16<E>, + /// 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<E>, + /// Section header table entry size. + pub e_shentsize: U16<E>, + /// 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<E>, + /// 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<E>, +} + +/// 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<E: Endian> { + /// Section name. + /// + /// This is an offset into the section header string table. + pub sh_name: U32<E>, + /// Section type. One of the `SHT_*` constants. + pub sh_type: U32<E>, + /// Section flags. A combination of the `SHF_*` constants. + pub sh_flags: U32<E>, + /// Section virtual address at execution. + pub sh_addr: U32<E>, + /// Section file offset. + pub sh_offset: U32<E>, + /// Section size in bytes. + pub sh_size: U32<E>, + /// Link to another section. + /// + /// The section relationship depends on the `sh_type` value. + pub sh_link: U32<E>, + /// Additional section information. + /// + /// The meaning of this field depends on the `sh_type` value. + pub sh_info: U32<E>, + /// Section alignment. + pub sh_addralign: U32<E>, + /// Entry size if the section holds a table. + pub sh_entsize: U32<E>, +} + +/// Section header. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct SectionHeader64<E: Endian> { + /// Section name. + /// + /// This is an offset into the section header string table. + pub sh_name: U32<E>, + /// Section type. One of the `SHT_*` constants. + pub sh_type: U32<E>, + /// Section flags. A combination of the `SHF_*` constants. + pub sh_flags: U64<E>, + /// Section virtual address at execution. + pub sh_addr: U64<E>, + /// Section file offset. + pub sh_offset: U64<E>, + /// Section size in bytes. + pub sh_size: U64<E>, + /// Link to another section. + /// + /// The section relationship depends on the `sh_type` value. + pub sh_link: U32<E>, + /// Additional section information. + /// + /// The meaning of this field depends on the `sh_type` value. + pub sh_info: U32<E>, + /// Section alignment. + pub sh_addralign: U64<E>, + /// Entry size if the section holds a table. + pub sh_entsize: U64<E>, +} + +// 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; +/// 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<E: Endian> { + /// Compression format. One of the `ELFCOMPRESS_*` values. + pub ch_type: U32Bytes<E>, + /// Uncompressed data size. + pub ch_size: U32Bytes<E>, + /// Uncompressed data alignment. + pub ch_addralign: U32Bytes<E>, +} + +/// 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<E: Endian> { + /// Compression format. One of the `ELFCOMPRESS_*` values. + pub ch_type: U32Bytes<E>, + /// Reserved. + pub ch_reserved: U32Bytes<E>, + /// Uncompressed data size. + pub ch_size: U64Bytes<E>, + /// Uncompressed data alignment. + pub ch_addralign: U64Bytes<E>, +} + +/// 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<E: Endian> { + /// Symbol name. + /// + /// This is an offset into the symbol string table. + pub st_name: U32<E>, + /// Symbol value. + pub st_value: U32<E>, + /// Symbol size. + pub st_size: U32<E>, + /// 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<E>, +} + +impl<E: Endian> Sym32<E> { + /// 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<E: Endian> { + /// Symbol name. + /// + /// This is an offset into the symbol string table. + pub st_name: U32<E>, + /// 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<E>, + /// Symbol value. + pub st_value: U64<E>, + /// Symbol size. + pub st_size: U64<E>, +} + +impl<E: Endian> Sym64<E> { + /// 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<E: Endian> { + /// Direct bindings, symbol bound to. + pub si_boundto: U16<E>, + /// Per symbol flags. + pub si_flags: U16<E>, +} + +/// Additional information about a `Sym64`. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Syminfo64<E: Endian> { + /// Direct bindings, symbol bound to. + pub si_boundto: U16<E>, + /// Per symbol flags. + pub si_flags: U16<E>, +} + +// 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<E: Endian> { + /// Relocation address. + pub r_offset: U32<E>, + /// Relocation type and symbol index. + pub r_info: U32<E>, +} + +impl<E: Endian> Rel32<E> { + /// 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<E> { + 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<E: Endian> { + /// Relocation address. + pub r_offset: U32<E>, + /// Relocation type and symbol index. + pub r_info: U32<E>, + /// Explicit addend. + pub r_addend: I32<E>, +} + +impl<E: Endian> Rela32<E> { + /// 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<E> { + 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<E: Endian> From<Rel32<E>> for Rela32<E> { + fn from(rel: Rel32<E>) -> 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<E: Endian> { + /// Relocation address. + pub r_offset: U64<E>, + /// Relocation type and symbol index. + pub r_info: U64<E>, +} + +impl<E: Endian> Rel64<E> { + /// 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<E> { + 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<E: Endian> From<Rel64<E>> for Rela64<E> { + fn from(rel: Rel64<E>) -> 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<E: Endian> { + /// Relocation address. + pub r_offset: U64<E>, + /// Relocation type and symbol index. + pub r_info: U64<E>, + /// Explicit addend. + pub r_addend: I64<E>, +} + +impl<E: Endian> Rela64<E> { + 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<E> { + 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<E: Endian> { + /// Segment type. One of the `PT_*` constants. + pub p_type: U32<E>, + /// Segment file offset. + pub p_offset: U32<E>, + /// Segment virtual address. + pub p_vaddr: U32<E>, + /// Segment physical address. + pub p_paddr: U32<E>, + /// Segment size in the file. + pub p_filesz: U32<E>, + /// Segment size in memory. + pub p_memsz: U32<E>, + /// Segment flags. A combination of the `PF_*` constants. + pub p_flags: U32<E>, + /// Segment alignment. + pub p_align: U32<E>, +} + +/// Program segment header. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ProgramHeader64<E: Endian> { + /// Segment type. One of the `PT_*` constants. + pub p_type: U32<E>, + /// Segment flags. A combination of the `PF_*` constants. + pub p_flags: U32<E>, + /// Segment file offset. + pub p_offset: U64<E>, + /// Segment virtual address. + pub p_vaddr: U64<E>, + /// Segment physical address. + pub p_paddr: U64<E>, + /// Segment size in the file. + pub p_filesz: U64<E>, + /// Segment size in memory. + pub p_memsz: U64<E>, + /// Segment alignment. + pub p_align: U64<E>, +} + +/// 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<E: Endian> { + /// Dynamic entry type. + pub d_tag: U32<E>, + /// Value (integer or address). + pub d_val: U32<E>, +} + +/// Dynamic section entry. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Dyn64<E: Endian> { + /// Dynamic entry type. + pub d_tag: U64<E>, + /// Value (integer or address). + pub d_val: U64<E>, +} + +// 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<E: Endian>(pub U16<E>); + +/// 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<E: Endian> { + /// Version revision + pub vd_version: U16<E>, + /// Version information + pub vd_flags: U16<E>, + /// Version Index + pub vd_ndx: U16<E>, + /// Number of associated aux entries + pub vd_cnt: U16<E>, + /// Version name hash value + pub vd_hash: U32<E>, + /// Offset in bytes to verdaux array + pub vd_aux: U32<E>, + /// Offset in bytes to next verdef entry + pub vd_next: U32<E>, +} + +// 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<E: Endian> { + /// Version or dependency names + pub vda_name: U32<E>, + /// Offset in bytes to next verdaux + pub vda_next: U32<E>, +} + +/// Version dependency. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Verneed<E: Endian> { + /// Version of structure + pub vn_version: U16<E>, + /// Number of associated aux entries + pub vn_cnt: U16<E>, + /// Offset of filename for this dependency + pub vn_file: U32<E>, + /// Offset in bytes to vernaux array + pub vn_aux: U32<E>, + /// Offset in bytes to next verneed entry + pub vn_next: U32<E>, +} + +// 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<E: Endian> { + /// Hash value of dependency name + pub vna_hash: U32<E>, + /// Dependency specific information + pub vna_flags: U16<E>, + /// Version Index + pub vna_other: U16<E>, + /// Dependency name string offset + pub vna_name: U32<E>, + /// Offset in bytes to next vernaux entry + pub vna_next: U32<E>, +} + +// 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<E: Endian> { + /// Length of the note's name. + /// + /// Some known names are defined by the `ELF_NOTE_*` constants. + pub n_namesz: U32<E>, + /// Length of the note's descriptor. + /// + /// The content of the descriptor depends on the note name and type. + pub n_descsz: U32<E>, + /// Type of the note. + /// + /// One of the `NT_*` constants. The note name determines which + /// `NT_*` constants are valid. + pub n_type: U32<E>, +} + +/// Note section entry header. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct NoteHeader64<E: Endian> { + /// Length of the note's name. + /// + /// Some known names are defined by the `ELF_NOTE_*` constants. + pub n_namesz: U32<E>, + /// Length of the note's descriptor. + /// + /// The content of the descriptor depends on the note name and type. + pub n_descsz: U32<E>, + /// Type of the note. + /// + /// One of the `NT_*` constants. The note name determines which + /// `NT_*` constants are valid. + pub n_type: U32<E>, +} + +/// 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_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<E: Endian> { + /// The number of hash buckets. + pub bucket_count: U32<E>, + /// The number of chain values. + pub chain_count: U32<E>, + // Array of hash bucket start indices. + // buckets: U32<E>[bucket_count] + // Array of hash chain links. An index of 0 terminates the chain. + // chains: U32<E>[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<E: Endian> { + /// The number of hash buckets. + pub bucket_count: U32<E>, + /// The symbol table index of the first symbol in the hash. + pub symbol_base: U32<E>, + /// The number of words in the bloom filter. + /// + /// Must be a non-zero power of 2. + pub bloom_count: U32<E>, + /// The bit shift count for the bloom filter. + pub bloom_shift: U32<E>, + // Array of bloom filter words. + // bloom_filters: U32<E>[bloom_count] or U64<E>[bloom_count] + // Array of hash bucket start indices. + // buckets: U32<E>[bucket_count] + // Array of hash values, one for each symbol starting at symbol_base. + // values: U32<E>[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; + +// 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 `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<cond>). +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<cond>.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<cond>). +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 `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; + +// 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 +/// insturction can be relaxed +pub const R_LARCH_RELAX: u32 = 100; + +// 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; + +#[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, +); diff --git a/third_party/rust/object/src/endian.rs b/third_party/rust/object/src/endian.rs new file mode 100644 index 0000000000..e4a36ba13f --- /dev/null +++ b/third_party/rust/object/src/endian.rs @@ -0,0 +1,831 @@ +//! 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<Self>; + + /// 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> { + 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<Self> { + 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<Self> { + 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<Self> { + 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<E: Endian> Pod for $struct_name<E> { } + )+ + } +} + +#[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<E: Endian>(u16, PhantomData<E>); + + impl<E: Endian> U16<E> { + /// Construct a new value given bytes that already have the required endianness. + pub 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<E: Endian>(u32, PhantomData<E>); + + impl<E: Endian> U32<E> { + /// Construct a new value given bytes that already have the required endianness. + pub 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<E: Endian>(u64, PhantomData<E>); + + impl<E: Endian> U64<E> { + /// Construct a new value given bytes that already have the required endianness. + pub 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<E: Endian>(i16, PhantomData<E>); + + impl<E: Endian> I16<E> { + /// Construct a new value given bytes that already have the required endianness. + pub 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<E: Endian>(i32, PhantomData<E>); + + impl<E: Endian> I32<E> { + /// Construct a new value given bytes that already have the required endianness. + pub 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<E: Endian>(i64, PhantomData<E>); + + impl<E: Endian> I64<E> { + /// Construct a new value given bytes that already have the required endianness. + pub 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<E: Endian> fmt::Debug for U16<E> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "U16({:x})", self.0) + } + } + + impl<E: Endian> fmt::Debug for U32<E> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "U32({:x})", self.0) + } + } + + impl<E: Endian> fmt::Debug for U64<E> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "U64({:x})", self.0) + } + } + + impl<E: Endian> fmt::Debug for I16<E> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "I16({:x})", self.0) + } + } + + impl<E: Endian> fmt::Debug for I32<E> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "I32({:x})", self.0) + } + } + + impl<E: Endian> fmt::Debug for I64<E> { + 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<E> = U16Bytes<E>; + +/// A `u32` value with an externally specified endianness of type `E`. +#[cfg(feature = "unaligned")] +pub type U32<E> = U32Bytes<E>; + +/// A `u64` value with an externally specified endianness of type `E`. +#[cfg(feature = "unaligned")] +pub type U64<E> = U64Bytes<E>; + +/// An `i16` value with an externally specified endianness of type `E`. +#[cfg(feature = "unaligned")] +pub type I16<E> = I16Bytes<E>; + +/// An `i32` value with an externally specified endianness of type `E`. +#[cfg(feature = "unaligned")] +pub type I32<E> = I32Bytes<E>; + +/// An `i64` value with an externally specified endianness of type `E`. +#[cfg(feature = "unaligned")] +pub type I64<E> = I64Bytes<E>; + +/// 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<E: Endian>([u8; 2], PhantomData<E>); + +impl<E: Endian> U16Bytes<E> { + /// Construct a new value given bytes that already have the required endianness. + pub 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<E: Endian>([u8; 4], PhantomData<E>); + +impl<E: Endian> U32Bytes<E> { + /// Construct a new value given bytes that already have the required endianness. + pub 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<E: Endian>([u8; 8], PhantomData<E>); + +impl<E: Endian> U64Bytes<E> { + /// Construct a new value given bytes that already have the required endianness. + pub 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<E: Endian>([u8; 2], PhantomData<E>); + +impl<E: Endian> I16Bytes<E> { + /// Construct a new value given bytes that already have the required endianness. + pub 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<E: Endian>([u8; 4], PhantomData<E>); + +impl<E: Endian> I32Bytes<E> { + /// Construct a new value given bytes that already have the required endianness. + pub 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<E: Endian>([u8; 8], PhantomData<E>); + +impl<E: Endian> I64Bytes<E> { + /// Construct a new value given bytes that already have the required endianness. + pub 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<E: Endian> fmt::Debug for U16Bytes<E> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "U16({:x}, {:x})", self.0[0], self.0[1],) + } +} + +impl<E: Endian> fmt::Debug for U32Bytes<E> { + 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<E: Endian> fmt::Debug for U64Bytes<E> { + 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<E: Endian> fmt::Debug for I16Bytes<E> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "I16({:x}, {:x})", self.0[0], self.0[1],) + } +} + +impl<E: Endian> fmt::Debug for I32Bytes<E> { + 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<E: Endian> fmt::Debug for I64Bytes<E> { + 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); diff --git a/third_party/rust/object/src/lib.rs b/third_party/rust/object/src/lib.rs new file mode 100644 index 0000000000..e17802c4fa --- /dev/null +++ b/third_party/rust/object/src/lib.rs @@ -0,0 +1,116 @@ +//! # `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 [pod] and [endian]. +//! +//! ## Unified read API +//! +//! The [read::Object] trait defines the unified interface. This trait is implemented +//! by [read::File], which allows reading any file format, as well as implementations +//! for each file format: [ELF](read::elf::ElfFile), [Mach-O](read::macho::MachOFile), +//! [COFF](read::coff::CoffFile), [PE](read::pe::PeFile), [Wasm](read::wasm::WasmFile), +//! [XCOFF](read::xcoff::XcoffFile). +//! +//! ## Low level read API +//! +//! In addition to the unified read API, the various `read` modules define helpers that +//! operate on the raw structs. These also provide traits that abstract over the differences +//! between 32-bit and 64-bit versions of the file format. +//! +//! ## Unified write API +//! +//! [write::Object] allows building a COFF/ELF/Mach-O/XCOFF relocatable object file and +//! then writing it out. +//! +//! ## Low level executable writers +//! +//! [write::elf::Writer] and [write::pe::Writer] allow writing executable files. +//! +//! ## Example for unified read API +//! ```no_run +//! # #[cfg(feature = "read")] +//! use object::{Object, ObjectSection}; +//! use std::error::Error; +//! use std::fs; +//! +//! /// Reads a file and displays the content of the ".boot" section. +//! fn main() -> Result<(), Box<dyn Error>> { +//! # #[cfg(all(feature = "read", feature = "std"))] { +//! let bin_data = fs::read("./multiboot2-binary.elf")?; +//! let obj_file = object::File::parse(&*bin_data)?; +//! if let Some(section) = obj_file.section_by_name(".boot") { +//! println!("{:#x?}", section.data()?); +//! } else { +//! eprintln!("section not available"); +//! } +//! # } +//! Ok(()) +//! } +//! ``` + +#![deny(missing_docs)] +#![deny(missing_debug_implementations)] +#![no_std] +#![warn(rust_2018_idioms)] +// Style. +#![allow(clippy::collapsible_if)] +#![allow(clippy::comparison_chain)] +#![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)] +// Worse readability sometimes. +#![allow(clippy::collapsible_else_if)] + +#[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 = "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; diff --git a/third_party/rust/object/src/macho.rs b/third_party/rust/object/src/macho.rs new file mode 100644 index 0000000000..3cd38e0eef --- /dev/null +++ b/third_party/rust/object/src/macho.rs @@ -0,0 +1,3307 @@ +//! 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: Endian> { + /// e.g. "dyld_v0 i386" + pub magic: [u8; 16], + /// file offset to first dyld_cache_mapping_info + pub mapping_offset: U32<E>, // offset: 0x10 + /// number of dyld_cache_mapping_info entries + pub mapping_count: U32<E>, // offset: 0x14 + /// file offset to first dyld_cache_image_info + pub images_offset: U32<E>, // offset: 0x18 + /// number of dyld_cache_image_info entries + pub images_count: U32<E>, // offset: 0x1c + /// base address of dyld when cache was built + pub dyld_base_address: U64<E>, // offset: 0x20 + /// + reserved1: [u8; 32], // offset: 0x28 + /// file offset of where local symbols are stored + pub local_symbols_offset: U64<E>, // offset: 0x48 + /// size of local symbols information + pub local_symbols_size: U64<E>, // 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<E>, // offset: 0x188 + /// number of dyld_subcache_info entries + pub subcaches_count: U32<E>, // 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<E>, // 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<E>, // offset: 0x1c4 +} + +/// Corresponds to struct dyld_cache_mapping_info from dyld_cache_format.h. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DyldCacheMappingInfo<E: Endian> { + /// + pub address: U64<E>, + /// + pub size: U64<E>, + /// + pub file_offset: U64<E>, + /// + pub max_prot: U32<E>, + /// + pub init_prot: U32<E>, +} + +/// Corresponds to struct dyld_cache_image_info from dyld_cache_format.h. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DyldCacheImageInfo<E: Endian> { + /// + pub address: U64<E>, + /// + pub mod_time: U64<E>, + /// + pub inode: U64<E>, + /// + pub path_file_offset: U32<E>, + /// + pub pad: U32<E>, +} + +/// Corresponds to a struct whose source code has not been published as of Nov 2021. +/// Added in the dyld cache version which shipped with macOS 12 / iOS 15. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DyldSubCacheInfo<E: Endian> { + /// The UUID of this subcache. + pub uuid: [u8; 16], + /// The size of this subcache plus all previous subcaches. + pub cumulative_size: U64<E>, +} + +// 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<BigEndian>, + /// number of structs that follow + pub nfat_arch: U32<BigEndian>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct FatArch32 { + /// cpu specifier (int) + pub cputype: U32<BigEndian>, + /// machine specifier (int) + pub cpusubtype: U32<BigEndian>, + /// file offset to this object file + pub offset: U32<BigEndian>, + /// size of this object file + pub size: U32<BigEndian>, + /// alignment as a power of 2 + pub align: U32<BigEndian>, +} + +/* + * 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<BigEndian>, + /// machine specifier (int) + pub cpusubtype: U32<BigEndian>, + /// file offset to this object file + pub offset: U64<BigEndian>, + /// size of this object file + pub size: U64<BigEndian>, + /// alignment as a power of 2 + pub align: U32<BigEndian>, + /// reserved + pub reserved: U32<BigEndian>, +} + +// 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<E: Endian> { + /// mach magic number identifier + pub magic: U32<BigEndian>, + /// cpu specifier + pub cputype: U32<E>, + /// machine specifier + pub cpusubtype: U32<E>, + /// type of file + pub filetype: U32<E>, + /// number of load commands + pub ncmds: U32<E>, + /// the size of all the load commands + pub sizeofcmds: U32<E>, + /// flags + pub flags: U32<E>, +} + +// 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<E: Endian> { + /// mach magic number identifier + pub magic: U32<BigEndian>, + /// cpu specifier + pub cputype: U32<E>, + /// machine specifier + pub cpusubtype: U32<E>, + /// type of file + pub filetype: U32<E>, + /// number of load commands + pub ncmds: U32<E>, + /// the size of all the load commands + pub sizeofcmds: U32<E>, + /// flags + pub flags: U32<E>, + /// reserved + pub reserved: U32<E>, +} + +// 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<E: Endian> { + /// Type of load command. + /// + /// One of the `LC_*` constants. + pub cmd: U32<E>, + /// Total size of command in bytes. + pub cmdsize: U32<E>, +} + +/* + * 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<E: Endian> { + /// offset to the string + pub offset: U32<E>, +} + +/// 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<E: Endian> { + /// LC_SEGMENT + pub cmd: U32<E>, + /// includes sizeof section structs + pub cmdsize: U32<E>, + /// segment name + pub segname: [u8; 16], + /// memory address of this segment + pub vmaddr: U32<E>, + /// memory size of this segment + pub vmsize: U32<E>, + /// file offset of this segment + pub fileoff: U32<E>, + /// amount to map from the file + pub filesize: U32<E>, + /// maximum VM protection + pub maxprot: U32<E>, + /// initial VM protection + pub initprot: U32<E>, + /// number of sections in segment + pub nsects: U32<E>, + /// flags + pub flags: U32<E>, +} + +/// 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<E: Endian> { + /// LC_SEGMENT_64 + pub cmd: U32<E>, + /// includes sizeof section_64 structs + pub cmdsize: U32<E>, + /// segment name + pub segname: [u8; 16], + /// memory address of this segment + pub vmaddr: U64<E>, + /// memory size of this segment + pub vmsize: U64<E>, + /// file offset of this segment + pub fileoff: U64<E>, + /// amount to map from the file + pub filesize: U64<E>, + /// maximum VM protection + pub maxprot: U32<E>, + /// initial VM protection + pub initprot: U32<E>, + /// number of sections in segment + pub nsects: U32<E>, + /// flags + pub flags: U32<E>, +} + +// 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 <reloc.h>. + */ +/// 32-bit section. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Section32<E: Endian> { + /// 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<E>, + /// size in bytes of this section + pub size: U32<E>, + /// file offset of this section + pub offset: U32<E>, + /// section alignment (power of 2) + pub align: U32<E>, + /// file offset of relocation entries + pub reloff: U32<E>, + /// number of relocation entries + pub nreloc: U32<E>, + /// flags (section type and attributes) + pub flags: U32<E>, + /// reserved (for offset or index) + pub reserved1: U32<E>, + /// reserved (for count or sizeof) + pub reserved2: U32<E>, +} + +/// 64-bit section. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Section64<E: Endian> { + /// 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<E>, + /// size in bytes of this section + pub size: U64<E>, + /// file offset of this section + pub offset: U32<E>, + /// section alignment (power of 2) + pub align: U32<E>, + /// file offset of relocation entries + pub reloff: U32<E>, + /// number of relocation entries + pub nreloc: U32<E>, + /// flags (section type and attributes) + pub flags: U32<E>, + /// reserved (for offset or index) + pub reserved1: U32<E>, + /// reserved (for count or sizeof) + pub reserved2: U32<E>, + /// reserved + pub reserved3: U32<E>, +} + +/* + * 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<E: Endian> { + /// library's target pathname + pub name: LcStr<E>, + /// library's minor version number + pub minor_version: U32<E>, + /// library's header address + pub header_addr: U32<E>, +} + +/* + * 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<E: Endian> { + /// LC_IDFVMLIB or LC_LOADFVMLIB + pub cmd: U32<E>, + /// includes pathname string + pub cmdsize: U32<E>, + /// the library identification + pub fvmlib: Fvmlib<E>, +} + +/* + * 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<E: Endian> { + /// library's path name + pub name: LcStr<E>, + /// library's build time stamp + pub timestamp: U32<E>, + /// library's current version number + pub current_version: U32<E>, + /// library's compatibility vers number + pub compatibility_version: U32<E>, +} + +/* + * 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<E: Endian> { + /// LC_ID_DYLIB, LC_LOAD_{,WEAK_}DYLIB, LC_REEXPORT_DYLIB + pub cmd: U32<E>, + /// includes pathname string + pub cmdsize: U32<E>, + /// the library identification + pub dylib: Dylib<E>, +} + +/* + * 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<E: Endian> { + /// LC_SUB_FRAMEWORK + pub cmd: U32<E>, + /// includes umbrella string + pub cmdsize: U32<E>, + /// the umbrella framework name + pub umbrella: LcStr<E>, +} + +/* + * 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<E: Endian> { + /// LC_SUB_CLIENT + pub cmd: U32<E>, + /// includes client string + pub cmdsize: U32<E>, + /// the client name + pub client: LcStr<E>, +} + +/* + * 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<E: Endian> { + /// LC_SUB_UMBRELLA + pub cmd: U32<E>, + /// includes sub_umbrella string + pub cmdsize: U32<E>, + /// the sub_umbrella framework name + pub sub_umbrella: LcStr<E>, +} + +/* + * 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<E: Endian> { + /// LC_SUB_LIBRARY + pub cmd: U32<E>, + /// includes sub_library string + pub cmdsize: U32<E>, + /// the sub_library name + pub sub_library: LcStr<E>, +} + +/* + * 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<E: Endian> { + /// LC_PREBOUND_DYLIB + pub cmd: U32<E>, + /// includes strings + pub cmdsize: U32<E>, + /// library's path name + pub name: LcStr<E>, + /// number of modules in library + pub nmodules: U32<E>, + /// bit vector of linked modules + pub linked_modules: LcStr<E>, +} + +/* + * 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<E: Endian> { + /// LC_ID_DYLINKER, LC_LOAD_DYLINKER or LC_DYLD_ENVIRONMENT + pub cmd: U32<E>, + /// includes pathname string + pub cmdsize: U32<E>, + /// dynamic linker's path name + pub name: LcStr<E>, +} + +/* + * 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 <machine/thread_status.h>. + * 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<E: Endian> { + /// LC_THREAD or LC_UNIXTHREAD + pub cmd: U32<E>, + /// total size of this command + pub cmdsize: U32<E>, + /* 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<E: Endian> { + /* for 32-bit architectures */ + /// LC_ROUTINES + pub cmd: U32<E>, + /// total size of this command + pub cmdsize: U32<E>, + /// address of initialization routine + pub init_address: U32<E>, + /// index into the module table that the init routine is defined in + pub init_module: U32<E>, + pub reserved1: U32<E>, + pub reserved2: U32<E>, + pub reserved3: U32<E>, + pub reserved4: U32<E>, + pub reserved5: U32<E>, + pub reserved6: U32<E>, +} + +/* + * The 64-bit routines command. Same use as above. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct RoutinesCommand64<E: Endian> { + /* for 64-bit architectures */ + /// LC_ROUTINES_64 + pub cmd: U32<E>, + /// total size of this command + pub cmdsize: U32<E>, + /// address of initialization routine + pub init_address: U64<E>, + /// index into the module table that the init routine is defined in + pub init_module: U64<E>, + pub reserved1: U64<E>, + pub reserved2: U64<E>, + pub reserved3: U64<E>, + pub reserved4: U64<E>, + pub reserved5: U64<E>, + pub reserved6: U64<E>, +} + +/* + * The `SymtabCommand` contains the offsets and sizes of the link-edit 4.3BSD + * "stab" style symbol table information as described in the header files + * <nlist.h> and <stab.h>. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct SymtabCommand<E: Endian> { + /// LC_SYMTAB + pub cmd: U32<E>, + /// sizeof(struct SymtabCommand) + pub cmdsize: U32<E>, + /// symbol table offset + pub symoff: U32<E>, + /// number of symbol table entries + pub nsyms: U32<E>, + /// string table offset + pub stroff: U32<E>, + /// string table size in bytes + pub strsize: U32<E>, +} + +/* + * 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<E: Endian> { + /// LC_DYSYMTAB + pub cmd: U32<E>, + /// sizeof(struct DysymtabCommand) + pub cmdsize: U32<E>, + + /* + * 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<E>, + /// number of local symbols + pub nlocalsym: U32<E>, + + /// index to externally defined symbols + pub iextdefsym: U32<E>, + /// number of externally defined symbols + pub nextdefsym: U32<E>, + + /// index to undefined symbols + pub iundefsym: U32<E>, + /// number of undefined symbols + pub nundefsym: U32<E>, + + /* + * 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<E>, + /// number of entries in table of contents + pub ntoc: U32<E>, + + /* + * 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<E>, + /// number of module table entries + pub nmodtab: U32<E>, + + /* + * 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<E>, + /// number of referenced symbol table entries + pub nextrefsyms: U32<E>, + + /* + * 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<E>, + /// number of indirect symbol table entries + pub nindirectsyms: U32<E>, + + /* + * 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<E>, + /// number of external relocation entries + pub nextrel: U32<E>, + + /* + * 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<E>, + /// number of local relocation entries + pub nlocrel: U32<E>, +} + +/* + * 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<E: Endian> { + /// the defined external symbol (index into the symbol table) + pub symbol_index: U32<E>, + /// index into the module table this symbol is defined in + pub module_index: U32<E>, +} + +/* a module table entry */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DylibModule32<E: Endian> { + /// the module name (index into string table) + pub module_name: U32<E>, + + /// index into externally defined symbols + pub iextdefsym: U32<E>, + /// number of externally defined symbols + pub nextdefsym: U32<E>, + /// index into reference symbol table + pub irefsym: U32<E>, + /// number of reference symbol table entries + pub nrefsym: U32<E>, + /// index into symbols for local symbols + pub ilocalsym: U32<E>, + /// number of local symbols + pub nlocalsym: U32<E>, + + /// index into external relocation entries + pub iextrel: U32<E>, + /// number of external relocation entries + pub nextrel: U32<E>, + + /// low 16 bits are the index into the init section, high 16 bits are the index into the term section + pub iinit_iterm: U32<E>, + /// low 16 bits are the number of init section entries, high 16 bits are the number of term section entries + pub ninit_nterm: U32<E>, + + /// for this module address of the start of the (__OBJC,__module_info) section + pub objc_module_info_addr: U32<E>, + /// for this module size of the (__OBJC,__module_info) section + pub objc_module_info_size: U32<E>, +} + +/* a 64-bit module table entry */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DylibModule64<E: Endian> { + /// the module name (index into string table) + pub module_name: U32<E>, + + /// index into externally defined symbols + pub iextdefsym: U32<E>, + /// number of externally defined symbols + pub nextdefsym: U32<E>, + /// index into reference symbol table + pub irefsym: U32<E>, + /// number of reference symbol table entries + pub nrefsym: U32<E>, + /// index into symbols for local symbols + pub ilocalsym: U32<E>, + /// number of local symbols + pub nlocalsym: U32<E>, + + /// index into external relocation entries + pub iextrel: U32<E>, + /// number of external relocation entries + pub nextrel: U32<E>, + + /// low 16 bits are the index into the init section, high 16 bits are the index into the term section + pub iinit_iterm: U32<E>, + /// low 16 bits are the number of init section entries, high 16 bits are the number of term section entries + pub ninit_nterm: U32<E>, + + /// for this module size of the (__OBJC,__module_info) section + pub objc_module_info_size: U32<E>, + /// for this module address of the start of the (__OBJC,__module_info) section + pub objc_module_info_addr: U64<E>, +} + +/* + * 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 + * <mach-o/nlist.h> as they are also used for symbol table entries. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct DylibReference<E: Endian> { + /* TODO: + uint32_t isym:24, /* index into the symbol table */ + flags:8; /* flags to indicate the type of reference */ + */ + pub bitfield: U32<E>, +} + +/* + * 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<E: Endian> { + /// LC_TWOLEVEL_HINTS + pub cmd: U32<E>, + /// sizeof(struct TwolevelHintsCommand) + pub cmdsize: U32<E>, + /// offset to the hint table + pub offset: U32<E>, + /// number of hints in the hint table + pub nhints: U32<E>, +} + +/* + * 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<E: Endian> { + /* TODO: + uint32_t + isub_image:8, /* index into the sub images */ + itoc:24; /* index into the table of contents */ + */ + pub bitfield: U32<E>, +} + +/* + * 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<E: Endian> { + /// LC_PREBIND_CKSUM + pub cmd: U32<E>, + /// sizeof(struct PrebindCksumCommand) + pub cmdsize: U32<E>, + /// the check sum or zero + pub cksum: U32<E>, +} + +/* + * 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<E: Endian> { + /// LC_UUID + pub cmd: U32<E>, + /// sizeof(struct UuidCommand) + pub cmdsize: U32<E>, + /// 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<E: Endian> { + /// LC_RPATH + pub cmd: U32<E>, + /// includes string + pub cmdsize: U32<E>, + /// path to add to run path + pub path: LcStr<E>, +} + +/* + * 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<E: Endian> { + /// `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<E>, + /// sizeof(struct LinkeditDataCommand) + pub cmdsize: U32<E>, + /// file offset of data in __LINKEDIT segment + pub dataoff: U32<E>, + /// file size of data in __LINKEDIT segment + pub datasize: U32<E>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct FilesetEntryCommand<E: Endian> { + // LC_FILESET_ENTRY + pub cmd: U32<E>, + /// includes id string + pub cmdsize: U32<E>, + /// memory address of the dylib + pub vmaddr: U64<E>, + /// file offset of the dylib + pub fileoff: U64<E>, + /// contained entry id + pub entry_id: LcStr<E>, + /// entry_id is 32-bits long, so this is the reserved padding + pub reserved: U32<E>, +} + +/* + * The EncryptionInfoCommand32 contains the file offset and size of an + * of an encrypted segment. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct EncryptionInfoCommand32<E: Endian> { + /// LC_ENCRYPTION_INFO + pub cmd: U32<E>, + /// sizeof(struct EncryptionInfoCommand32) + pub cmdsize: U32<E>, + /// file offset of encrypted range + pub cryptoff: U32<E>, + /// file size of encrypted range + pub cryptsize: U32<E>, + /// which enryption system, 0 means not-encrypted yet + pub cryptid: U32<E>, +} + +/* + * 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<E: Endian> { + /// LC_ENCRYPTION_INFO_64 + pub cmd: U32<E>, + /// sizeof(struct EncryptionInfoCommand64) + pub cmdsize: U32<E>, + /// file offset of encrypted range + pub cryptoff: U32<E>, + /// file size of encrypted range + pub cryptsize: U32<E>, + /// which enryption system, 0 means not-encrypted yet + pub cryptid: U32<E>, + /// padding to make this struct's size a multiple of 8 bytes + pub pad: U32<E>, +} + +/* + * The VersionMinCommand contains the min OS version on which this + * binary was built to run. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct VersionMinCommand<E: Endian> { + /// LC_VERSION_MIN_MACOSX or LC_VERSION_MIN_IPHONEOS or LC_VERSION_MIN_WATCHOS or LC_VERSION_MIN_TVOS + pub cmd: U32<E>, + /// sizeof(struct VersionMinCommand) + pub cmdsize: U32<E>, + /// X.Y.Z is encoded in nibbles xxxx.yy.zz + pub version: U32<E>, + /// X.Y.Z is encoded in nibbles xxxx.yy.zz + pub sdk: U32<E>, +} + +/* + * 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<E: Endian> { + /// LC_BUILD_VERSION + pub cmd: U32<E>, + /// sizeof(struct BuildVersionCommand) plus ntools * sizeof(struct BuildToolVersion) + pub cmdsize: U32<E>, + /// platform + pub platform: U32<E>, + /// X.Y.Z is encoded in nibbles xxxx.yy.zz + pub minos: U32<E>, + /// X.Y.Z is encoded in nibbles xxxx.yy.zz + pub sdk: U32<E>, + /// number of tool entries following this + pub ntools: U32<E>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct BuildToolVersion<E: Endian> { + /// enum for the tool + pub tool: U32<E>, + /// version number of the tool + pub version: U32<E>, +} + +/* 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; + +/* 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<E: Endian> { + /// LC_DYLD_INFO or LC_DYLD_INFO_ONLY + pub cmd: U32<E>, + /// sizeof(struct DyldInfoCommand) + pub cmdsize: U32<E>, + + /* + * 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: + * <seg-index, seg-offset, type> + * 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<E>, + /// size of rebase info + pub rebase_size: U32<E>, + + /* + * 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: + * <seg-index, seg-offset, type, symbol-library-ordinal, symbol-name, addend> + * 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<E>, + /// size of binding info + pub bind_size: U32<E>, + + /* + * 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<E>, + /// size of weak binding info + pub weak_bind_size: U32<E>, + + /* + * 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<E>, + /// size of lazy binding infs + pub lazy_bind_size: U32<E>, + + /* + * 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<E>, + /// size of lazy binding infs + pub export_size: U32<E>, +} + +/* + * 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<E: Endian> { + /// LC_LINKER_OPTION only used in MH_OBJECT filetypes + pub cmd: U32<E>, + pub cmdsize: U32<E>, + /// number of strings + pub count: U32<E>, + /* 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 <symseg.h>. + * 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<E: Endian> { + /// LC_SYMSEG + pub cmd: U32<E>, + /// sizeof(struct SymsegCommand) + pub cmdsize: U32<E>, + /// symbol segment offset + pub offset: U32<E>, + /// symbol segment size in bytes + pub size: U32<E>, +} + +/* + * 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<E: Endian> { + /// LC_IDENT + pub cmd: U32<E>, + /// strings that follow this command + pub cmdsize: U32<E>, +} + +/* + * 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<E: Endian> { + /// LC_FVMFILE + pub cmd: U32<E>, + /// includes pathname string + pub cmdsize: U32<E>, + /// files pathname + pub name: LcStr<E>, + /// files virtual address + pub header_addr: U32<E>, +} + +/* + * 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<E: Endian> { + /// LC_MAIN only used in MH_EXECUTE filetypes + pub cmd: U32<E>, + /// 24 + pub cmdsize: U32<E>, + /// file (__TEXT) offset of main() + pub entryoff: U64<E>, + /// if not zero, initial stack size + pub stacksize: U64<E>, +} + +/* + * 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<E: Endian> { + /// LC_SOURCE_VERSION + pub cmd: U32<E>, + /// 16 + pub cmdsize: U32<E>, + /// A.B.C.D.E packed as a24.b10.c10.d10.e10 + pub version: U64<E>, +} + +/* + * 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<E: Endian> { + /// from mach_header to start of data range + pub offset: U32<E>, + /// number of bytes in data range + pub length: U16<E>, + /// a DICE_KIND_* value + pub kind: U16<E>, +} +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<E: Endian> +{ + 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<E: Endian> { + /// LC_NOTE + pub cmd: U32<E>, + /// sizeof(struct NoteCommand) + pub cmdsize: U32<E>, + /// owner name for this LC_NOTE + pub data_owner: [u8; 16], + /// file offset of this data + pub offset: U64<E>, + /// length of data region + pub size: U64<E>, +} + +// Definitions from "/usr/include/mach-o/nlist.h". + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Nlist32<E: Endian> { + /// index into the string table + pub n_strx: U32<E>, + /// type flag, see below + pub n_type: u8, + /// section number or NO_SECT + pub n_sect: u8, + /// see <mach-o/stab.h> + pub n_desc: U16<E>, + /// value of this symbol (or stab offset) + pub n_value: U32<E>, +} + +/* + * This is the symbol table entry structure for 64-bit architectures. + */ +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Nlist64<E: Endian> { + /// index into the string table + pub n_strx: U32<E>, + /// type flag, see below + pub n_type: u8, + /// section number or NO_SECT + pub n_sect: u8, + /// see <mach-o/stab.h> + pub n_desc: U16<E>, + /// value of this symbol (or stab offset) + // Note: 4 byte alignment has been observed in practice. + pub n_value: U64Bytes<E>, +} + +/* + * 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 <mach-o/stab.h> + */ + +/* + * 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 <nlist.h> 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<E: Endian> { + pub r_word0: U32<E>, + pub r_word1: U32<E>, +} + +impl<E: Endian> Relocation<E> { + /// 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<E: Endian>(self, endian: E) -> Relocation<E> { + 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 <mach-o/loader.h> 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<E: Endian>(self, endian: E) -> Relocation<E> { + 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 <mach-o/reloc.h> 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, + DyldSubCacheInfo, + 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, +); diff --git a/third_party/rust/object/src/pe.rs b/third_party/rust/object/src/pe.rs new file mode 100644 index 0000000000..f274d2270b --- /dev/null +++ b/third_party/rust/object/src/pe.rs @@ -0,0 +1,3054 @@ +//! 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<LE>, + /// Bytes on last page of file + pub e_cblp: U16<LE>, + /// Pages in file + pub e_cp: U16<LE>, + /// Relocations + pub e_crlc: U16<LE>, + /// Size of header in paragraphs + pub e_cparhdr: U16<LE>, + /// Minimum extra paragraphs needed + pub e_minalloc: U16<LE>, + /// Maximum extra paragraphs needed + pub e_maxalloc: U16<LE>, + /// Initial (relative) SS value + pub e_ss: U16<LE>, + /// Initial SP value + pub e_sp: U16<LE>, + /// Checksum + pub e_csum: U16<LE>, + /// Initial IP value + pub e_ip: U16<LE>, + /// Initial (relative) CS value + pub e_cs: U16<LE>, + /// File address of relocation table + pub e_lfarlc: U16<LE>, + /// Overlay number + pub e_ovno: U16<LE>, + /// Reserved words + pub e_res: [U16<LE>; 4], + /// OEM identifier (for e_oeminfo) + pub e_oemid: U16<LE>, + /// OEM information; e_oemid specific + pub e_oeminfo: U16<LE>, + /// Reserved words + pub e_res2: [U16<LE>; 10], + /// File address of new exe header + pub e_lfanew: U32<LE>, +} + +/// OS/2 .EXE header +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageOs2Header { + /// Magic number + pub ne_magic: U16<LE>, + /// Version number + pub ne_ver: i8, + /// Revision number + pub ne_rev: i8, + /// Offset of Entry Table + pub ne_enttab: U16<LE>, + /// Number of bytes in Entry Table + pub ne_cbenttab: U16<LE>, + /// Checksum of whole file + pub ne_crc: I32<LE>, + /// Flag word + pub ne_flags: U16<LE>, + /// Automatic data segment number + pub ne_autodata: U16<LE>, + /// Initial heap allocation + pub ne_heap: U16<LE>, + /// Initial stack allocation + pub ne_stack: U16<LE>, + /// Initial CS:IP setting + pub ne_csip: I32<LE>, + /// Initial SS:SP setting + pub ne_sssp: I32<LE>, + /// Count of file segments + pub ne_cseg: U16<LE>, + /// Entries in Module Reference Table + pub ne_cmod: U16<LE>, + /// Size of non-resident name table + pub ne_cbnrestab: U16<LE>, + /// Offset of Segment Table + pub ne_segtab: U16<LE>, + /// Offset of Resource Table + pub ne_rsrctab: U16<LE>, + /// Offset of resident name table + pub ne_restab: U16<LE>, + /// Offset of Module Reference Table + pub ne_modtab: U16<LE>, + /// Offset of Imported Names Table + pub ne_imptab: U16<LE>, + /// Offset of Non-resident Names Table + pub ne_nrestab: I32<LE>, + /// Count of movable entries + pub ne_cmovent: U16<LE>, + /// Segment alignment shift count + pub ne_align: U16<LE>, + /// Count of resource segments + pub ne_cres: U16<LE>, + /// Target Operating system + pub ne_exetyp: u8, + /// Other .EXE flags + pub ne_flagsothers: u8, + /// offset to return thunks + pub ne_pretthunks: U16<LE>, + /// offset to segment ref. bytes + pub ne_psegrefbytes: U16<LE>, + /// Minimum code swap area size + pub ne_swaparea: U16<LE>, + /// Expected Windows version number + pub ne_expver: U16<LE>, +} + +/// Windows VXD header +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageVxdHeader { + /// Magic number + pub e32_magic: U16<LE>, + /// 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<LE>, + /// The CPU type + pub e32_cpu: U16<LE>, + /// The OS type + pub e32_os: U16<LE>, + /// Module version + pub e32_ver: U32<LE>, + /// Module flags + pub e32_mflags: U32<LE>, + /// Module # pages + pub e32_mpages: U32<LE>, + /// Object # for instruction pointer + pub e32_startobj: U32<LE>, + /// Extended instruction pointer + pub e32_eip: U32<LE>, + /// Object # for stack pointer + pub e32_stackobj: U32<LE>, + /// Extended stack pointer + pub e32_esp: U32<LE>, + /// VXD page size + pub e32_pagesize: U32<LE>, + /// Last page size in VXD + pub e32_lastpagesize: U32<LE>, + /// Fixup section size + pub e32_fixupsize: U32<LE>, + /// Fixup section checksum + pub e32_fixupsum: U32<LE>, + /// Loader section size + pub e32_ldrsize: U32<LE>, + /// Loader section checksum + pub e32_ldrsum: U32<LE>, + /// Object table offset + pub e32_objtab: U32<LE>, + /// Number of objects in module + pub e32_objcnt: U32<LE>, + /// Object page map offset + pub e32_objmap: U32<LE>, + /// Object iterated data map offset + pub e32_itermap: U32<LE>, + /// Offset of Resource Table + pub e32_rsrctab: U32<LE>, + /// Number of resource entries + pub e32_rsrccnt: U32<LE>, + /// Offset of resident name table + pub e32_restab: U32<LE>, + /// Offset of Entry Table + pub e32_enttab: U32<LE>, + /// Offset of Module Directive Table + pub e32_dirtab: U32<LE>, + /// Number of module directives + pub e32_dircnt: U32<LE>, + /// Offset of Fixup Page Table + pub e32_fpagetab: U32<LE>, + /// Offset of Fixup Record Table + pub e32_frectab: U32<LE>, + /// Offset of Import Module Name Table + pub e32_impmod: U32<LE>, + /// Number of entries in Import Module Name Table + pub e32_impmodcnt: U32<LE>, + /// Offset of Import Procedure Name Table + pub e32_impproc: U32<LE>, + /// Offset of Per-Page Checksum Table + pub e32_pagesum: U32<LE>, + /// Offset of Enumerated Data Pages + pub e32_datapage: U32<LE>, + /// Number of preload pages + pub e32_preload: U32<LE>, + /// Offset of Non-resident Names Table + pub e32_nrestab: U32<LE>, + /// Size of Non-resident Name Table + pub e32_cbnrestab: U32<LE>, + /// Non-resident Name Table Checksum + pub e32_nressum: U32<LE>, + /// Object # for automatic data object + pub e32_autodata: U32<LE>, + /// Offset of the debugging information + pub e32_debuginfo: U32<LE>, + /// The length of the debugging info. in bytes + pub e32_debuglen: U32<LE>, + /// Number of instance pages in preload section of VXD file + pub e32_instpreload: U32<LE>, + /// Number of instance pages in demand load section of VXD file + pub e32_instdemand: U32<LE>, + /// Size of heap - for 16-bit apps + pub e32_heapsize: U32<LE>, + /// Reserved words + pub e32_res3: [u8; 12], + pub e32_winresoff: U32<LE>, + pub e32_winreslen: U32<LE>, + /// Device ID for VxD + pub e32_devid: U16<LE>, + /// DDK version for VxD + pub e32_ddkver: U16<LE>, +} + +/// 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<LE>, + pub masked_count: U32<LE>, +} + +// +// File header format. +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageFileHeader { + pub machine: U16<LE>, + pub number_of_sections: U16<LE>, + pub time_date_stamp: U32<LE>, + pub pointer_to_symbol_table: U32<LE>, + pub number_of_symbols: U32<LE>, + pub size_of_optional_header: U16<LE>, + pub characteristics: U16<LE>, +} + +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; +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; + +// +// Directory format. +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageDataDirectory { + pub virtual_address: U32<LE>, + pub size: U32<LE>, +} + +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<LE>, + pub major_linker_version: u8, + pub minor_linker_version: u8, + pub size_of_code: U32<LE>, + pub size_of_initialized_data: U32<LE>, + pub size_of_uninitialized_data: U32<LE>, + pub address_of_entry_point: U32<LE>, + pub base_of_code: U32<LE>, + pub base_of_data: U32<LE>, + + // NT additional fields. + pub image_base: U32<LE>, + pub section_alignment: U32<LE>, + pub file_alignment: U32<LE>, + pub major_operating_system_version: U16<LE>, + pub minor_operating_system_version: U16<LE>, + pub major_image_version: U16<LE>, + pub minor_image_version: U16<LE>, + pub major_subsystem_version: U16<LE>, + pub minor_subsystem_version: U16<LE>, + pub win32_version_value: U32<LE>, + pub size_of_image: U32<LE>, + pub size_of_headers: U32<LE>, + pub check_sum: U32<LE>, + pub subsystem: U16<LE>, + pub dll_characteristics: U16<LE>, + pub size_of_stack_reserve: U32<LE>, + pub size_of_stack_commit: U32<LE>, + pub size_of_heap_reserve: U32<LE>, + pub size_of_heap_commit: U32<LE>, + pub loader_flags: U32<LE>, + pub number_of_rva_and_sizes: U32<LE>, + //pub data_directory: [ImageDataDirectory; IMAGE_NUMBEROF_DIRECTORY_ENTRIES], +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageRomOptionalHeader { + pub magic: U16<LE>, + pub major_linker_version: u8, + pub minor_linker_version: u8, + pub size_of_code: U32<LE>, + pub size_of_initialized_data: U32<LE>, + pub size_of_uninitialized_data: U32<LE>, + pub address_of_entry_point: U32<LE>, + pub base_of_code: U32<LE>, + pub base_of_data: U32<LE>, + pub base_of_bss: U32<LE>, + pub gpr_mask: U32<LE>, + pub cpr_mask: [U32<LE>; 4], + pub gp_value: U32<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageOptionalHeader64 { + pub magic: U16<LE>, + pub major_linker_version: u8, + pub minor_linker_version: u8, + pub size_of_code: U32<LE>, + pub size_of_initialized_data: U32<LE>, + pub size_of_uninitialized_data: U32<LE>, + pub address_of_entry_point: U32<LE>, + pub base_of_code: U32<LE>, + pub image_base: U64<LE>, + pub section_alignment: U32<LE>, + pub file_alignment: U32<LE>, + pub major_operating_system_version: U16<LE>, + pub minor_operating_system_version: U16<LE>, + pub major_image_version: U16<LE>, + pub minor_image_version: U16<LE>, + pub major_subsystem_version: U16<LE>, + pub minor_subsystem_version: U16<LE>, + pub win32_version_value: U32<LE>, + pub size_of_image: U32<LE>, + pub size_of_headers: U32<LE>, + pub check_sum: U32<LE>, + pub subsystem: U16<LE>, + pub dll_characteristics: U16<LE>, + pub size_of_stack_reserve: U64<LE>, + pub size_of_stack_commit: U64<LE>, + pub size_of_heap_reserve: U64<LE>, + pub size_of_heap_commit: U64<LE>, + pub loader_flags: U32<LE>, + pub number_of_rva_and_sizes: U32<LE>, + //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<LE>, + pub file_header: ImageFileHeader, + pub optional_header: ImageOptionalHeader64, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageNtHeaders32 { + pub signature: U32<LE>, + 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<LE> { + U32::from_bytes(self.0[0..4].try_into().unwrap()) + } + + #[inline] + pub fn data2(self) -> U16<LE> { + U16::from_bytes(self.0[4..6].try_into().unwrap()) + } + + #[inline] + pub fn data3(self) -> U16<LE> { + 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<LE>, + /// Must be 0xffff + pub sig2: U16<LE>, + /// >= 1 (implies the ClsId field is present) + pub version: U16<LE>, + pub machine: U16<LE>, + pub time_date_stamp: U32<LE>, + /// Used to invoke CoCreateInstance + pub class_id: ClsId, + /// Size of data that follows the header + pub size_of_data: U32<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct AnonObjectHeaderV2 { + /// Must be IMAGE_FILE_MACHINE_UNKNOWN + pub sig1: U16<LE>, + /// Must be 0xffff + pub sig2: U16<LE>, + /// >= 2 (implies the Flags field is present - otherwise V1) + pub version: U16<LE>, + pub machine: U16<LE>, + pub time_date_stamp: U32<LE>, + /// Used to invoke CoCreateInstance + pub class_id: ClsId, + /// Size of data that follows the header + pub size_of_data: U32<LE>, + /// 0x1 -> contains metadata + pub flags: U32<LE>, + /// Size of CLR metadata + pub meta_data_size: U32<LE>, + /// Offset of CLR metadata + pub meta_data_offset: U32<LE>, +} + +/// 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<LE>, + /// Must be 0xffff + pub sig2: U16<LE>, + /// >= 2 (implies the Flags field is present) + pub version: U16<LE>, + /// Actual machine - IMAGE_FILE_MACHINE_xxx + pub machine: U16<LE>, + pub time_date_stamp: U32<LE>, + /// Must be `ANON_OBJECT_HEADER_BIGOBJ_CLASS_ID`. + pub class_id: ClsId, + /// Size of data that follows the header + pub size_of_data: U32<LE>, + /// 0x1 -> contains metadata + pub flags: U32<LE>, + /// Size of CLR metadata + pub meta_data_size: U32<LE>, + /// Offset of CLR metadata + pub meta_data_offset: U32<LE>, + + /* bigobj specifics */ + /// extended from WORD + pub number_of_sections: U32<LE>, + pub pointer_to_symbol_table: U32<LE>, + pub number_of_symbols: U32<LE>, +} + +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<LE>, + pub virtual_address: U32<LE>, + pub size_of_raw_data: U32<LE>, + pub pointer_to_raw_data: U32<LE>, + pub pointer_to_relocations: U32<LE>, + pub pointer_to_linenumbers: U32<LE>, + pub number_of_relocations: U16<LE>, + pub number_of_linenumbers: U16<LE>, + pub characteristics: U32<LE>, +} + +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<LE>, + pub section_number: U16Bytes<LE>, + pub typ: U16Bytes<LE>, + 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<LE>, + pub section_number: I32Bytes<LE>, + pub typ: U16Bytes<LE>, + 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<LE>, + /// 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<LE>, + pub total_size: U32Bytes<LE>, + pub pointer_to_linenumber: U32Bytes<LE>, + pub pointer_to_next_function: U32Bytes<LE>, + 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<LE>, + pub unused2: [u8; 6], + pub pointer_to_next_function: U32Bytes<LE>, + 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<LE>, + pub weak_search_type: U32Bytes<LE>, +} + +/// 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<LE>, + /// number of relocation entries + pub number_of_relocations: U16Bytes<LE>, + /// number of line numbers + pub number_of_linenumbers: U16Bytes<LE>, + /// checksum for communal + pub check_sum: U32Bytes<LE>, + /// section number to associate with + pub number: U16Bytes<LE>, + /// communal selection type + pub selection: u8, + pub reserved: u8, + /// high bits of the section number + pub high_number: U16Bytes<LE>, +} + +// 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<LE>, +} + +// +// 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<LE>, + pub symbol_table_index: U32Bytes<LE>, + pub typ: U16Bytes<LE>, +} + +// +// 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<LE>, + /// Line number. + pub linenumber: U16Bytes<LE>, +} + +// +// Based relocation format. +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageBaseRelocation { + pub virtual_address: U32<LE>, + pub size_of_block: U32<LE>, + // pub type_offset[1]: U16<LE>, +} + +// +// 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"!<arch>\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"/<HYBRIDMAP>/ "; + +#[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<LE>, + pub time_date_stamp: U32<LE>, + pub major_version: U16<LE>, + pub minor_version: U16<LE>, + pub name: U32<LE>, + pub base: U32<LE>, + pub number_of_functions: U32<LE>, + pub number_of_names: U32<LE>, + /// RVA from base of image + pub address_of_functions: U32<LE>, + /// RVA from base of image + pub address_of_names: U32<LE>, + /// RVA from base of image + pub address_of_name_ordinals: U32<LE>, +} + +// +// Import Format +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageImportByName { + pub hint: U16<LE>, + //pub name: [i8; 1], +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageThunkData64(pub U64<LE>); +/* + union { +/// PBYTE + pub forwarder_string: U64<LE>, +/// PDWORD + pub function: U64<LE>, + pub ordinal: U64<LE>, +/// PIMAGE_IMPORT_BY_NAME + pub address_of_data: U64<LE>, + } u1; +*/ + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageThunkData32(pub U32<LE>); +/* + union { +/// PBYTE + pub forwarder_string: U32<LE>, +/// PDWORD + pub function: U32<LE>, + pub ordinal: U32<LE>, +/// PIMAGE_IMPORT_BY_NAME + pub address_of_data: U32<LE>, + } 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<LE>, + pub end_address_of_raw_data: U64<LE>, + /// PDWORD + pub address_of_index: U64<LE>, + /// PIMAGE_TLS_CALLBACK *; + pub address_of_call_backs: U64<LE>, + pub size_of_zero_fill: U32<LE>, + pub characteristics: U32<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageTlsDirectory32 { + pub start_address_of_raw_data: U32<LE>, + pub end_address_of_raw_data: U32<LE>, + /// PDWORD + pub address_of_index: U32<LE>, + /// PIMAGE_TLS_CALLBACK * + pub address_of_call_backs: U32<LE>, + pub size_of_zero_fill: U32<LE>, + pub characteristics: U32<LE>, +} + +#[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<LE>, + /// 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<LE>, + /// -1 if no forwarders + pub forwarder_chain: U32Bytes<LE>, + pub name: U32Bytes<LE>, + /// RVA to IAT (if bound this IAT has actual addresses) + pub first_thunk: U32Bytes<LE>, +} + +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<LE>, + pub offset_module_name: U16<LE>, + pub number_of_module_forwarder_refs: U16<LE>, + // Array of zero or more IMAGE_BOUND_FORWARDER_REF follows +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageBoundForwarderRef { + pub time_date_stamp: U32<LE>, + pub offset_module_name: U16<LE>, + pub reserved: U16<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageDelayloadDescriptor { + pub attributes: U32<LE>, + + /// RVA to the name of the target library (NULL-terminate ASCII string) + pub dll_name_rva: U32<LE>, + /// RVA to the HMODULE caching location (PHMODULE) + pub module_handle_rva: U32<LE>, + /// RVA to the start of the IAT (PIMAGE_THUNK_DATA) + pub import_address_table_rva: U32<LE>, + /// RVA to the start of the name table (PIMAGE_THUNK_DATA::AddressOfData) + pub import_name_table_rva: U32<LE>, + /// RVA to an optional bound IAT + pub bound_import_address_table_rva: U32<LE>, + /// RVA to an optional unload info table + pub unload_information_table_rva: U32<LE>, + /// 0 if not bound, otherwise, date/time of the target DLL + pub time_date_stamp: U32<LE>, +} + +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<LE>, + pub time_date_stamp: U32<LE>, + pub major_version: U16<LE>, + pub minor_version: U16<LE>, + pub number_of_named_entries: U16<LE>, + pub number_of_id_entries: U16<LE>, +} + +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<LE>, + pub offset_to_data_or_directory: U32<LE>, +} + +// +// 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<LE>, + //pub name_string: [i8; 1], +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageResourceDirStringU { + pub length: U16<LE>, + //pub name_string: [U16<LE>; 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<LE>, + pub size: U32<LE>, + pub code_page: U32<LE>, + pub reserved: U32<LE>, +} + +// 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<LE>, + /// 0xFFFF means not available + pub catalog: U16<LE>, + pub catalog_offset: U32<LE>, + /// Additional bitmask to be defined later + pub reserved: U32<LE>, +} + +// +// Dynamic value relocation table in loadconfig +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageDynamicRelocationTable { + pub version: U32<LE>, + pub size: U32<LE>, + // DynamicRelocations: [ImageDynamicRelocation; 0], +} + +// +// Dynamic value relocation entries following IMAGE_DYNAMIC_RELOCATION_TABLE +// + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageDynamicRelocation32 { + pub symbol: U32<LE>, + pub base_reloc_size: U32<LE>, + // BaseRelocations: [ImageBaseRelocation; 0], +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageDynamicRelocation64 { + pub symbol: U64<LE>, + pub base_reloc_size: U32<LE>, + // BaseRelocations: [ImageBaseRelocation; 0], +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageDynamicRelocation32V2 { + pub header_size: U32<LE>, + pub fixup_info_size: U32<LE>, + pub symbol: U32<LE>, + pub symbol_group: U32<LE>, + pub flags: U32<LE>, + // ... variable length header fields + // pub fixup_info: [u8; fixup_info_size] +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageDynamicRelocation64V2 { + pub header_size: U32<LE>, + pub fixup_info_size: U32<LE>, + pub symbol: U64<LE>, + pub symbol_group: U32<LE>, + pub flags: U32<LE>, + // ... 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<LE>, + pub epilogue_byte_count: u8, + pub branch_descriptor_element_size: u8, + pub branch_descriptor_count: U16Bytes<LE>, + // 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<LE>, + pub time_date_stamp: U32<LE>, + pub major_version: U16<LE>, + pub minor_version: U16<LE>, + pub global_flags_clear: U32<LE>, + pub global_flags_set: U32<LE>, + pub critical_section_default_timeout: U32<LE>, + pub de_commit_free_block_threshold: U32<LE>, + pub de_commit_total_free_threshold: U32<LE>, + /// VA + pub lock_prefix_table: U32<LE>, + pub maximum_allocation_size: U32<LE>, + pub virtual_memory_threshold: U32<LE>, + pub process_heap_flags: U32<LE>, + pub process_affinity_mask: U32<LE>, + pub csd_version: U16<LE>, + pub dependent_load_flags: U16<LE>, + /// VA + pub edit_list: U32<LE>, + /// VA + pub security_cookie: U32<LE>, + /// VA + pub sehandler_table: U32<LE>, + pub sehandler_count: U32<LE>, + /// VA + pub guard_cf_check_function_pointer: U32<LE>, + /// VA + pub guard_cf_dispatch_function_pointer: U32<LE>, + /// VA + pub guard_cf_function_table: U32<LE>, + pub guard_cf_function_count: U32<LE>, + pub guard_flags: U32<LE>, + pub code_integrity: ImageLoadConfigCodeIntegrity, + /// VA + pub guard_address_taken_iat_entry_table: U32<LE>, + pub guard_address_taken_iat_entry_count: U32<LE>, + /// VA + pub guard_long_jump_target_table: U32<LE>, + pub guard_long_jump_target_count: U32<LE>, + /// VA + pub dynamic_value_reloc_table: U32<LE>, + pub chpe_metadata_pointer: U32<LE>, + /// VA + pub guard_rf_failure_routine: U32<LE>, + /// VA + pub guard_rf_failure_routine_function_pointer: U32<LE>, + pub dynamic_value_reloc_table_offset: U32<LE>, + pub dynamic_value_reloc_table_section: U16<LE>, + pub reserved2: U16<LE>, + /// VA + pub guard_rf_verify_stack_pointer_function_pointer: U32<LE>, + pub hot_patch_table_offset: U32<LE>, + pub reserved3: U32<LE>, + /// VA + pub enclave_configuration_pointer: U32<LE>, + /// VA + pub volatile_metadata_pointer: U32<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageLoadConfigDirectory64 { + pub size: U32<LE>, + pub time_date_stamp: U32<LE>, + pub major_version: U16<LE>, + pub minor_version: U16<LE>, + pub global_flags_clear: U32<LE>, + pub global_flags_set: U32<LE>, + pub critical_section_default_timeout: U32<LE>, + pub de_commit_free_block_threshold: U64<LE>, + pub de_commit_total_free_threshold: U64<LE>, + /// VA + pub lock_prefix_table: U64<LE>, + pub maximum_allocation_size: U64<LE>, + pub virtual_memory_threshold: U64<LE>, + pub process_affinity_mask: U64<LE>, + pub process_heap_flags: U32<LE>, + pub csd_version: U16<LE>, + pub dependent_load_flags: U16<LE>, + /// VA + pub edit_list: U64<LE>, + /// VA + pub security_cookie: U64<LE>, + /// VA + pub sehandler_table: U64<LE>, + pub sehandler_count: U64<LE>, + /// VA + pub guard_cf_check_function_pointer: U64<LE>, + /// VA + pub guard_cf_dispatch_function_pointer: U64<LE>, + /// VA + pub guard_cf_function_table: U64<LE>, + pub guard_cf_function_count: U64<LE>, + pub guard_flags: U32<LE>, + pub code_integrity: ImageLoadConfigCodeIntegrity, + /// VA + pub guard_address_taken_iat_entry_table: U64<LE>, + pub guard_address_taken_iat_entry_count: U64<LE>, + /// VA + pub guard_long_jump_target_table: U64<LE>, + pub guard_long_jump_target_count: U64<LE>, + /// VA + pub dynamic_value_reloc_table: U64<LE>, + /// VA + pub chpe_metadata_pointer: U64<LE>, + /// VA + pub guard_rf_failure_routine: U64<LE>, + /// VA + pub guard_rf_failure_routine_function_pointer: U64<LE>, + pub dynamic_value_reloc_table_offset: U32<LE>, + pub dynamic_value_reloc_table_section: U16<LE>, + pub reserved2: U16<LE>, + /// VA + pub guard_rf_verify_stack_pointer_function_pointer: U64<LE>, + pub hot_patch_table_offset: U32<LE>, + pub reserved3: U32<LE>, + /// VA + pub enclave_configuration_pointer: U64<LE>, + /// VA + pub volatile_metadata_pointer: U64<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageHotPatchInfo { + pub version: U32<LE>, + pub size: U32<LE>, + pub sequence_number: U32<LE>, + pub base_image_list: U32<LE>, + pub base_image_count: U32<LE>, + /// Version 2 and later + pub buffer_offset: U32<LE>, + /// Version 3 and later + pub extra_patch_size: U32<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageHotPatchBase { + pub sequence_number: U32<LE>, + pub flags: U32<LE>, + pub original_time_date_stamp: U32<LE>, + pub original_check_sum: U32<LE>, + pub code_integrity_info: U32<LE>, + pub code_integrity_size: U32<LE>, + pub patch_table: U32<LE>, + /// Version 2 and later + pub buffer_offset: U32<LE>, +} + +#[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<LE>, + 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<LE>, + pub unwind_data: U32<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageArm64RuntimeFunctionEntry { + pub begin_address: U32<LE>, + pub unwind_data: U32<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageAlpha64RuntimeFunctionEntry { + pub begin_address: U64<LE>, + pub end_address: U64<LE>, + pub exception_handler: U64<LE>, + pub handler_data: U64<LE>, + pub prolog_end_address: U64<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageAlphaRuntimeFunctionEntry { + pub begin_address: U32<LE>, + pub end_address: U32<LE>, + pub exception_handler: U32<LE>, + pub handler_data: U32<LE>, + pub prolog_end_address: U32<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageRuntimeFunctionEntry { + pub begin_address: U32<LE>, + pub end_address: U32<LE>, + pub unwind_info_address_or_data: U32<LE>, +} + +// +// 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<LE>, + pub minimum_required_config_size: U32<LE>, + pub policy_flags: U32<LE>, + pub number_of_imports: U32<LE>, + pub import_list: U32<LE>, + pub import_entry_size: U32<LE>, + pub family_id: [u8; IMAGE_ENCLAVE_SHORT_ID_LENGTH], + pub image_id: [u8; IMAGE_ENCLAVE_SHORT_ID_LENGTH], + pub image_version: U32<LE>, + pub security_version: U32<LE>, + pub enclave_size: U32<LE>, + pub number_of_threads: U32<LE>, + pub enclave_flags: U32<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageEnclaveConfig64 { + pub size: U32<LE>, + pub minimum_required_config_size: U32<LE>, + pub policy_flags: U32<LE>, + pub number_of_imports: U32<LE>, + pub import_list: U32<LE>, + pub import_entry_size: U32<LE>, + pub family_id: [u8; IMAGE_ENCLAVE_SHORT_ID_LENGTH], + pub image_id: [u8; IMAGE_ENCLAVE_SHORT_ID_LENGTH], + pub image_version: U32<LE>, + pub security_version: U32<LE>, + pub enclave_size: U64<LE>, + pub number_of_threads: U32<LE>, + pub enclave_flags: U32<LE>, +} + +//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<LE>, + pub minimum_security_version: U32<LE>, + 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<LE>, + pub reserved: U32<LE>, +} + +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<LE>, + pub time_date_stamp: U32<LE>, + pub major_version: U16<LE>, + pub minor_version: U16<LE>, + pub typ: U32<LE>, + pub size_of_data: U32<LE>, + pub address_of_raw_data: U32<LE>, + pub pointer_to_raw_data: U32<LE>, +} + +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<LE>, + pub lva_to_first_symbol: U32<LE>, + pub number_of_linenumbers: U32<LE>, + pub lva_to_first_linenumber: U32<LE>, + pub rva_to_first_byte_of_code: U32<LE>, + pub rva_to_last_byte_of_code: U32<LE>, + pub rva_to_first_byte_of_data: U32<LE>, + pub rva_to_last_byte_of_data: U32<LE>, +} + +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<LE>, +/// # bytes in function + pub cb_proc_size: U32<LE>, +/// # bytes in locals/4 + pub cdw_locals: U32<LE>, +/// # bytes in params/4 + pub cdw_params: U16<LE>, +/// # 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<LE>, + /// total length of record, rounded to four byte multiple. + pub length: U32<LE>, + /// 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<LE>, + pub ending_address: U32<LE>, + pub end_of_prologue: U32<LE>, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageFunctionEntry64 { + pub starting_address: U64<LE>, + pub ending_address: U64<LE>, + pub end_of_prologue_or_unwind_info_address: U64<LE>, +} + +// +// 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<LE>, + pub flags: U16<LE>, + pub machine: U16<LE>, + pub characteristics: U16<LE>, + pub time_date_stamp: U32<LE>, + pub check_sum: U32<LE>, + pub image_base: U32<LE>, + pub size_of_image: U32<LE>, + pub number_of_sections: U32<LE>, + pub exported_names_size: U32<LE>, + pub debug_directory_size: U32<LE>, + pub section_alignment: U32<LE>, + pub reserved: [U32<LE>; 2], +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct NonPagedDebugInfo { + pub signature: U16<LE>, + pub flags: U16<LE>, + pub size: U32<LE>, + pub machine: U16<LE>, + pub characteristics: U16<LE>, + pub time_date_stamp: U32<LE>, + pub check_sum: U32<LE>, + pub size_of_image: U32<LE>, + pub image_base: U64<LE>, + //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<LE>, +} +*/ + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ImageArchitectureEntry { + /// RVA of instruction to fixup + pub fixup_inst_rva: U32<LE>, + /// fixup instruction (see alphaops.h) + pub new_inst: U32<LE>, +} + +// 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<LE>, + /// Must be IMPORT_OBJECT_HDR_SIG2. + pub sig2: U16<LE>, + pub version: U16<LE>, + pub machine: U16<LE>, + /// Time/date stamp + pub time_date_stamp: U32<LE>, + /// particularly useful for incremental links + pub size_of_data: U32<LE>, + + /// if grf & IMPORT_OBJECT_ORDINAL + pub ordinal_or_hint: U16<LE>, + + // WORD Type : 2; + // WORD NameType : 3; + // WORD Reserved : 11; + pub name_type: U16<LE>, +} + +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<LE>, + pub major_runtime_version: U16<LE>, + pub minor_runtime_version: U16<LE>, + + // Symbol table and startup information + pub meta_data: ImageDataDirectory, + pub flags: U32<LE>, + + // 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<LE>, + + // 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, +); diff --git a/third_party/rust/object/src/pod.rs b/third_party/rust/object/src/pod.rs new file mode 100644 index 0000000000..8ee78164f5 --- /dev/null +++ b/third_party/rust/object/src/pod.rs @@ -0,0 +1,239 @@ +//! 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<T> = result::Result<T, ()>; + +/// 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 a byte slice to a `Pod` type. +/// +/// Returns the type and the tail of the slice. +#[inline] +pub fn from_bytes<T: Pod>(data: &[u8]) -> Result<(&T, &[u8])> { + let size = mem::size_of::<T>(); + let tail = data.get(size..).ok_or(())?; + let ptr = data.as_ptr(); + if (ptr as usize) % mem::align_of::<T>() != 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 a mutable byte slice to a `Pod` type. +/// +/// Returns the type and the tail of the slice. +#[inline] +pub fn from_bytes_mut<T: Pod>(data: &mut [u8]) -> Result<(&mut T, &mut [u8])> { + let size = mem::size_of::<T>(); + 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::<T>() != 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 a byte slice to a slice of a `Pod` type. +/// +/// Returns the type slice and the tail of the byte slice. +#[inline] +pub fn slice_from_bytes<T: Pod>(data: &[u8], count: usize) -> Result<(&[T], &[u8])> { + let size = count.checked_mul(mem::size_of::<T>()).ok_or(())?; + let tail = data.get(size..).ok_or(())?; + let ptr = data.as_ptr(); + if (ptr as usize) % mem::align_of::<T>() != 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 a mutable byte slice to a slice of a `Pod` type. +/// +/// Returns the type slice and the tail of the byte slice. +#[inline] +pub fn slice_from_bytes_mut<T: Pod>( + data: &mut [u8], + count: usize, +) -> Result<(&mut [T], &mut [u8])> { + let size = count.checked_mul(mem::size_of::<T>()).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::<T>() != 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 a `Pod` type to a byte slice. +#[inline] +pub fn bytes_of<T: Pod>(val: &T) -> &[u8] { + let size = mem::size_of::<T>(); + // 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<T: Pod>(val: &mut T) -> &mut [u8] { + let size = mem::size_of::<T>(); + // 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<T: Pod>(val: &[T]) -> &[u8] { + let size = val.len().wrapping_mul(mem::size_of::<T>()); + // 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<T: Pod>(val: &mut [T]) -> &mut [u8] { + let size = val.len().wrapping_mul(mem::size_of::<T>()); + // 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); + +#[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::<u32>(bytes).unwrap(); + let (y_mut, tail_mut) = from_bytes_mut::<u32>(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::<u16>(bytes).unwrap(); + let (y_mut, tail_mut) = from_bytes_mut::<u16>(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::<u16>(&bytes[2..]).unwrap(); + let (y_mut, tail_mut) = from_bytes_mut::<u16>(&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::<u16>(&bytes[1..]), Err(())); + assert_eq!(from_bytes::<u16>(&bytes[3..]), Err(())); + assert_eq!(from_bytes::<u16>(&bytes[4..]), Err(())); + assert_eq!(from_bytes_mut::<u16>(&mut bytes_mut[1..]), Err(())); + assert_eq!(from_bytes_mut::<u16>(&mut bytes_mut[3..]), Err(())); + assert_eq!(from_bytes_mut::<u16>(&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::<u16>(bytes, 4).unwrap(); + let (y_mut, tail_mut) = slice_from_bytes_mut::<u16>(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::<u16>(&bytes[2..], 2).unwrap(); + let (y_mut, tail_mut) = slice_from_bytes::<u16>(&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::<u16>(bytes, 5), Err(())); + assert_eq!(slice_from_bytes::<u16>(&bytes[2..], 4), Err(())); + assert_eq!(slice_from_bytes::<u16>(&bytes[1..], 2), Err(())); + assert_eq!(slice_from_bytes_mut::<u16>(bytes_mut, 5), Err(())); + assert_eq!(slice_from_bytes_mut::<u16>(&mut bytes_mut[2..], 4), Err(())); + assert_eq!(slice_from_bytes_mut::<u16>(&mut bytes_mut[1..], 2), Err(())); + } +} diff --git a/third_party/rust/object/src/read/any.rs b/third_party/rust/object/src/read/any.rs new file mode 100644 index 0000000000..2e147c6727 --- /dev/null +++ b/third_party/rust/object/src/read/any.rs @@ -0,0 +1,1314 @@ +use alloc::fmt; +use alloc::vec::Vec; +use core::marker::PhantomData; + +#[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, Result, + SectionFlags, SectionIndex, SectionKind, SegmentFlags, SymbolFlags, SymbolIndex, SymbolKind, + SymbolMap, SymbolMapName, SymbolScope, SymbolSection, +}; +#[allow(unused_imports)] +use crate::{AddressSize, Endian, Endianness}; + +/// 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. +/// +/// 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<Self> { + 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: Endian>( + image: &macho::DyldCacheImage<'data, 'cache, E, R>, + ) -> Result<Self> { + Ok(match image.cache.architecture().address_size() { + Some(AddressSize::U64) => { + File::MachO64(macho::MachOFile64::parse_dyld_cache_image(image)?) + } + Some(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, 'file, R> Object<'data, 'file> for File<'data, R> +where + 'data: 'file, + R: 'file + ReadRef<'data>, +{ + type Segment = Segment<'data, 'file, R>; + type SegmentIterator = SegmentIterator<'data, 'file, R>; + type Section = Section<'data, 'file, R>; + type SectionIterator = SectionIterator<'data, 'file, R>; + type Comdat = Comdat<'data, 'file, R>; + type ComdatIterator = ComdatIterator<'data, 'file, R>; + type Symbol = Symbol<'data, 'file, R>; + type SymbolIterator = SymbolIterator<'data, 'file, R>; + type SymbolTable = SymbolTable<'data, 'file, R>; + type DynamicRelocationIterator = DynamicRelocationIterator<'data, 'file, R>; + + fn architecture(&self) -> Architecture { + with_inner!(self, File, |x| x.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(&'file self) -> SegmentIterator<'data, 'file, R> { + SegmentIterator { + inner: map_inner!(self, File, SegmentIteratorInternal, |x| x.segments()), + } + } + + fn section_by_name_bytes(&'file self, section_name: &[u8]) -> Option<Section<'data, 'file, R>> { + map_inner_option!(self, File, SectionInternal, |x| x + .section_by_name_bytes(section_name)) + .map(|inner| Section { inner }) + } + + fn section_by_index(&'file self, index: SectionIndex) -> Result<Section<'data, 'file, R>> { + map_inner_option!(self, File, SectionInternal, |x| x.section_by_index(index)) + .map(|inner| Section { inner }) + } + + fn sections(&'file self) -> SectionIterator<'data, 'file, R> { + SectionIterator { + inner: map_inner!(self, File, SectionIteratorInternal, |x| x.sections()), + } + } + + fn comdats(&'file self) -> ComdatIterator<'data, 'file, R> { + ComdatIterator { + inner: map_inner!(self, File, ComdatIteratorInternal, |x| x.comdats()), + } + } + + fn symbol_by_index(&'file self, index: SymbolIndex) -> Result<Symbol<'data, 'file, R>> { + map_inner_option!(self, File, SymbolInternal, |x| x + .symbol_by_index(index) + .map(|x| (x, PhantomData))) + .map(|inner| Symbol { inner }) + } + + fn symbols(&'file self) -> SymbolIterator<'data, 'file, R> { + SymbolIterator { + inner: map_inner!(self, File, SymbolIteratorInternal, |x| ( + x.symbols(), + PhantomData + )), + } + } + + fn symbol_table(&'file self) -> Option<SymbolTable<'data, 'file, R>> { + map_inner_option!(self, File, SymbolTableInternal, |x| x + .symbol_table() + .map(|x| (x, PhantomData))) + .map(|inner| SymbolTable { inner }) + } + + fn dynamic_symbols(&'file self) -> SymbolIterator<'data, 'file, R> { + SymbolIterator { + inner: map_inner!(self, File, SymbolIteratorInternal, |x| ( + x.dynamic_symbols(), + PhantomData + )), + } + } + + fn dynamic_symbol_table(&'file self) -> Option<SymbolTable<'data, 'file, R>> { + 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(&'file self) -> Option<DynamicRelocationIterator<'data, 'file, R>> { + 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(&'file self) -> Option<DynamicRelocationIterator<'data, 'file, R>> { + None + } + + fn symbol_map(&self) -> SymbolMap<SymbolMapName<'data>> { + 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<Vec<Import<'data>>> { + with_inner!(self, File, |x| x.imports()) + } + + fn exports(&self) -> Result<Vec<Export<'data>>> { + 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<Option<[u8; 16]>> { + with_inner!(self, File, |x| x.mach_uuid()) + } + + #[inline] + fn build_id(&self) -> Result<Option<&'data [u8]>> { + with_inner!(self, File, |x| x.build_id()) + } + + #[inline] + fn gnu_debuglink(&self) -> Result<Option<(&'data [u8], u32)>> { + with_inner!(self, File, |x| x.gnu_debuglink()) + } + + #[inline] + fn gnu_debugaltlink(&self) -> Result<Option<(&'data [u8], &'data [u8])>> { + with_inner!(self, File, |x| x.gnu_debugaltlink()) + } + + #[inline] + fn pdb_info(&self) -> Result<Option<CodeView<'_>>> { + 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 over the segments of 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<Self::Item> { + next_inner!(self.inner, SegmentIteratorInternal, SegmentInternal) + .map(|inner| Segment { inner }) + } +} + +/// A segment of a `File`. +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", &"<invalid>"); + } + } + 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<Option<&'data [u8]>> { + with_inner!(self.inner, SegmentInternal, |x| x.data_range(address, size)) + } + + fn name_bytes(&self) -> Result<Option<&[u8]>> { + with_inner!(self.inner, SegmentInternal, |x| x.name_bytes()) + } + + fn name(&self) -> Result<Option<&str>> { + with_inner!(self.inner, SegmentInternal, |x| x.name()) + } + + fn flags(&self) -> SegmentFlags { + with_inner!(self.inner, SegmentInternal, |x| x.flags()) + } +} + +/// An iterator of the sections of 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<Self::Item> { + next_inner!(self.inner, SectionIteratorInternal, SectionInternal) + .map(|inner| Section { inner }) + } +} + +/// A Section of a File +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", &"<invalid>"); + } + } + s.field("name", &self.name().unwrap_or("<invalid>")) + .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<Option<&'data [u8]>> { + with_inner!(self.inner, SectionInternal, |x| x.data_range(address, size)) + } + + fn compressed_file_range(&self) -> Result<CompressedFileRange> { + with_inner!(self.inner, SectionInternal, |x| x.compressed_file_range()) + } + + fn compressed_data(&self) -> Result<CompressedData<'data>> { + with_inner!(self.inner, SectionInternal, |x| x.compressed_data()) + } + + fn name_bytes(&self) -> Result<&[u8]> { + with_inner!(self.inner, SectionInternal, |x| x.name_bytes()) + } + + fn name(&self) -> Result<&str> { + with_inner!(self.inner, SectionInternal, |x| x.name()) + } + + fn segment_name_bytes(&self) -> Result<Option<&[u8]>> { + with_inner!(self.inner, SectionInternal, |x| x.segment_name_bytes()) + } + + fn segment_name(&self) -> Result<Option<&str>> { + with_inner!(self.inner, SectionInternal, |x| x.segment_name()) + } + + fn kind(&self) -> SectionKind { + with_inner!(self.inner, SectionInternal, |x| x.kind()) + } + + fn relocations(&self) -> SectionRelocationIterator<'data, 'file, R> { + SectionRelocationIterator { + inner: map_inner!( + self.inner, + SectionInternal, + SectionRelocationIteratorInternal, + |x| x.relocations() + ), + } + } + + fn flags(&self) -> SectionFlags { + with_inner!(self.inner, SectionInternal, |x| x.flags()) + } +} + +/// An iterator of the COMDAT section groups of 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<Self::Item> { + next_inner!(self.inner, ComdatIteratorInternal, ComdatInternal) + .map(|inner| Comdat { inner }) + } +} + +/// A COMDAT section group of a `File`. +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("<invalid>")) + .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<&[u8]> { + with_inner!(self.inner, ComdatInternal, |x| x.name_bytes()) + } + + fn name(&self) -> Result<&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 over COMDAT section entries. +#[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<Self::Item> { + with_inner_mut!(self.inner, ComdatSectionIteratorInternal, |x| x.next()) + } +} + +/// A symbol table. +#[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<R>)), + #[cfg(feature = "coff")] + CoffBig((coff::CoffBigSymbolTable<'data, 'file, R>, PhantomData<R>)), + #[cfg(feature = "elf")] + Elf32( + ( + elf::ElfSymbolTable32<'data, 'file, Endianness, R>, + PhantomData<R>, + ), + ), + #[cfg(feature = "elf")] + Elf64( + ( + elf::ElfSymbolTable64<'data, 'file, Endianness, R>, + PhantomData<R>, + ), + ), + #[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<R>)), + #[cfg(feature = "pe")] + Pe64((coff::CoffSymbolTable<'data, 'file, R>, PhantomData<R>)), + #[cfg(feature = "wasm")] + Wasm((wasm::WasmSymbolTable<'data, 'file>, PhantomData<R>)), + #[cfg(feature = "xcoff")] + Xcoff32((xcoff::XcoffSymbolTable32<'data, 'file, R>, PhantomData<R>)), + #[cfg(feature = "xcoff")] + Xcoff64((xcoff::XcoffSymbolTable64<'data, 'file, R>, PhantomData<R>)), +} + +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<Self::Symbol> { + map_inner_option!(self.inner, SymbolTableInternal, SymbolInternal, |x| x + .0 + .symbol_by_index(index) + .map(|x| (x, PhantomData))) + .map(|inner| Symbol { inner }) + } +} + +/// An iterator over symbol table entries. +#[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<R>)), + #[cfg(feature = "coff")] + CoffBig((coff::CoffBigSymbolIterator<'data, 'file, R>, PhantomData<R>)), + #[cfg(feature = "elf")] + Elf32( + ( + elf::ElfSymbolIterator32<'data, 'file, Endianness, R>, + PhantomData<R>, + ), + ), + #[cfg(feature = "elf")] + Elf64( + ( + elf::ElfSymbolIterator64<'data, 'file, Endianness, R>, + PhantomData<R>, + ), + ), + #[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<R>)), + #[cfg(feature = "pe")] + Pe64((coff::CoffSymbolIterator<'data, 'file, R>, PhantomData<R>)), + #[cfg(feature = "wasm")] + Wasm((wasm::WasmSymbolIterator<'data, 'file>, PhantomData<R>)), + #[cfg(feature = "xcoff")] + Xcoff32( + ( + xcoff::XcoffSymbolIterator32<'data, 'file, R>, + PhantomData<R>, + ), + ), + #[cfg(feature = "xcoff")] + Xcoff64( + ( + xcoff::XcoffSymbolIterator64<'data, 'file, R>, + PhantomData<R>, + ), + ), +} + +impl<'data, 'file, R: ReadRef<'data>> Iterator for SymbolIterator<'data, 'file, R> { + type Item = Symbol<'data, 'file, R>; + + fn next(&mut self) -> Option<Self::Item> { + map_inner_option_mut!(self.inner, SymbolIteratorInternal, SymbolInternal, |iter| { + iter.0.next().map(|x| (x, PhantomData)) + }) + .map(|inner| Symbol { inner }) + } +} + +/// A symbol table entry. +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<R>)), + #[cfg(feature = "coff")] + CoffBig((coff::CoffBigSymbol<'data, 'file, R>, PhantomData<R>)), + #[cfg(feature = "elf")] + Elf32( + ( + elf::ElfSymbol32<'data, 'file, Endianness, R>, + PhantomData<R>, + ), + ), + #[cfg(feature = "elf")] + Elf64( + ( + elf::ElfSymbol64<'data, 'file, Endianness, R>, + PhantomData<R>, + ), + ), + #[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<R>)), + #[cfg(feature = "pe")] + Pe64((coff::CoffSymbol<'data, 'file, R>, PhantomData<R>)), + #[cfg(feature = "wasm")] + Wasm((wasm::WasmSymbol<'data, 'file>, PhantomData<R>)), + #[cfg(feature = "xcoff")] + Xcoff32((xcoff::XcoffSymbol32<'data, 'file, R>, PhantomData<R>)), + #[cfg(feature = "xcoff")] + Xcoff64((xcoff::XcoffSymbol64<'data, 'file, R>, PhantomData<R>)), +} + +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("<invalid>")) + .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<SectionIndex, SymbolIndex> { + with_inner!(self.inner, SymbolInternal, |x| x.0.flags()) + } +} + +/// An iterator over dynamic relocation entries. +#[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<Self::Item> { + 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 over section relocation entries. +#[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<Self::Item> { + with_inner_mut!(self.inner, SectionRelocationIteratorInternal, |x| x.next()) + } +} diff --git a/third_party/rust/object/src/read/archive.rs b/third_party/rust/object/src/read/archive.rs new file mode 100644 index 0000000000..f5aaa9b190 --- /dev/null +++ b/third_party/rust/object/src/read/archive.rs @@ -0,0 +1,739 @@ +//! Support for archive files. + +use core::convert::TryInto; + +use crate::archive; +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], +} + +impl<'data, R: ReadRef<'data>> ArchiveFile<'data, R> { + /// Parse the archive header and special members. + pub fn parse(data: R) -> read::Result<Self> { + 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")?; + + if magic == archive::AIX_BIG_MAGIC { + return Self::parse_aixbig(data); + } else if magic != archive::MAGIC { + 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: &[], + }; + + // 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, &[])?; + 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, &[])?; + 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, &[])?; + if member.name == b"//" { + // COFF names table. + file.names = member.data(data)?; + 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, &[])?; + 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<Self> { + let mut tail = 0; + + let file_header = data + .read::<archive::AixFileHeader>(&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: &[], + }; + + // Read the span of symbol table. + 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::<u8>(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::<archive::AixMemberOffset>(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 + } + + /// 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, + } + } +} + +/// 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], +} + +impl<'data, R: ReadRef<'data>> Iterator for ArchiveMemberIterator<'data, R> { + type Item = read::Result<ArchiveMember<'data>>; + + fn next(&mut self) -> Option<Self::Item> { + 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); + 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], + 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<R: ReadRef<'data>>( + data: R, + offset: &mut u64, + names: &'data [u8], + ) -> read::Result<Self> { + let header = data + .read::<archive::Header>(offset) + .read_error("Invalid archive member header")?; + if header.terminator != archive::TERMINATOR { + return Err(Error("Invalid archive terminator")); + } + + let mut file_offset = *offset; + let mut file_size = + parse_u64_digits(&header.size, 10).read_error("Invalid archive member size")?; + *offset = offset + .checked_add(file_size) + .read_error("Archive member size is too large")?; + // Entries are padded to an even number of bytes. + if (file_size & 1) != 0 { + *offset = offset.saturating_add(1); + } + + 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 { + let name_len = memchr::memchr(b'/', &header.name) + .or_else(|| memchr::memchr(b' ', &header.name)) + .unwrap_or(header.name.len()); + &header.name[..name_len] + }; + + 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<R: ReadRef<'data>>( + data: R, + index: &archive::AixMemberOffset, + ) -> read::Result<Self> { + 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<R: ReadRef<'data>>(data: R, mut offset: u64) -> read::Result<Self> { + // The format was described at + // https://www.ibm.com/docs/en/aix/7.3?topic=formats-ar-file-format-big + let header = data + .read::<archive::AixHeader>(&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<u64> { + 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<u64> { + 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<u64> { + 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<u64> { + match &self.header { + MemberHeader::Common(header) => parse_u64_digits(&header.mode, 8), + MemberHeader::AixBig(header) => parse_u64_digits(&header.mode, 8), + } + } + + /// Return the offset and size of the file data. + pub fn file_range(&self) -> (u64, u64) { + (self.offset, self.size) + } + + /// Return the file data. + #[inline] + pub fn data<R: ReadRef<'data>>(&self, data: R) -> read::Result<&'data [u8]> { + data.read_bytes_at(self.offset, self.size) + .read_error("Archive member size is too large") + } +} + +// Ignores bytes starting from the first space. +fn parse_u64_digits(digits: &[u8], radix: u32) -> Option<u64> { + 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) +} + +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 name = match memchr::memchr2(b'/', b'\0', name_data) { + Some(len) => &name_data[..len], + None => name_data, + }; + Ok(name) +} + +/// Modifies `data` 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"!<arch>\n"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Unknown); + + let data = b"\ + !<arch>\n\ + / 4 `\n\ + 0000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Gnu); + + let data = b"\ + !<arch>\n\ + // 4 `\n\ + 0000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Gnu); + + let data = b"\ + !<arch>\n\ + / 4 `\n\ + 0000\ + // 4 `\n\ + 0000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Gnu); + + let data = b"\ + !<arch>\n\ + /SYM64/ 4 `\n\ + 0000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Gnu64); + + let data = b"\ + !<arch>\n\ + /SYM64/ 4 `\n\ + 0000\ + // 4 `\n\ + 0000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Gnu64); + + let data = b"\ + !<arch>\n\ + __.SYMDEF 4 `\n\ + 0000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Bsd); + + let data = b"\ + !<arch>\n\ + #1/9 13 `\n\ + __.SYMDEF0000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Bsd); + + let data = b"\ + !<arch>\n\ + #1/16 20 `\n\ + __.SYMDEF SORTED0000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Bsd); + + let data = b"\ + !<arch>\n\ + __.SYMDEF_64 4 `\n\ + 0000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Bsd64); + + let data = b"\ + !<arch>\n\ + #1/12 16 `\n\ + __.SYMDEF_640000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Bsd64); + + let data = b"\ + !<arch>\n\ + #1/19 23 `\n\ + __.SYMDEF_64 SORTED0000"; + let archive = ArchiveFile::parse(&data[..]).unwrap(); + assert_eq!(archive.kind(), ArchiveKind::Bsd64); + + let data = b"\ + !<arch>\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"\ + <bigaf>\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); + } + + #[test] + fn gnu_names() { + let data = b"\ + !<arch>\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 bsd_names() { + let data = b"\ + !<arch>\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"\ + <bigaf>\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()); + } +} diff --git a/third_party/rust/object/src/read/coff/comdat.rs b/third_party/rust/object/src/read/coff/comdat.rs new file mode 100644 index 0000000000..22e061a236 --- /dev/null +++ b/third_party/rust/object/src/read/coff/comdat.rs @@ -0,0 +1,207 @@ +use 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 over the COMDAT section groups of a `CoffBigFile`. +pub type CoffBigComdatIterator<'data, 'file, R = &'data [u8]> = + CoffComdatIterator<'data, 'file, R, pe::AnonObjectHeaderBigobj>; + +/// An iterator over the COMDAT section groups of a `CoffFile`. +#[derive(Debug)] +pub struct CoffComdatIterator< + 'data, + 'file, + R: ReadRef<'data> = &'data [u8], + Coff: CoffHeader = pe::ImageFileHeader, +> { + pub(super) file: &'file CoffFile<'data, R, Coff>, + pub(super) index: usize, +} + +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<Self::Item> { + loop { + let index = self.index; + let symbol = self.file.common.symbols.symbol(index).ok()?; + self.index += 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 of a `CoffBigFile`. +pub type CoffBigComdat<'data, 'file, R = &'data [u8]> = + CoffComdat<'data, 'file, R, pe::AnonObjectHeaderBigobj>; + +/// A COMDAT section group of a `CoffFile`. +#[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: usize, + ) -> Option<CoffComdat<'data, 'file, R, Coff>> { + // 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 += 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: SymbolIndex(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<&[u8]> { + // Find the name of first symbol referring to the section. + self.symbol.name(self.file.common.symbols.strings()) + } + + #[inline] + fn name(&self) -> Result<&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: 0, + } + } +} + +/// An iterator over the sections in a COMDAT section group of a `CoffBigFile`. +pub type CoffBigComdatSectionIterator<'data, 'file, R = &'data [u8]> = + CoffComdatSectionIterator<'data, 'file, R, pe::AnonObjectHeaderBigobj>; + +/// An iterator over the sections in a COMDAT section group of 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: usize, +} + +impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> Iterator + for CoffComdatSectionIterator<'data, 'file, R, Coff> +{ + type Item = SectionIndex; + + fn next(&mut self) -> Option<Self::Item> { + // 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 += 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)); + } + } + } + } +} diff --git a/third_party/rust/object/src/read/coff/file.rs b/third_party/rust/object/src/read/coff/file.rs new file mode 100644 index 0000000000..4219f8f027 --- /dev/null +++ b/third_party/rust/object/src/read/coff/file.rs @@ -0,0 +1,364 @@ +use alloc::vec::Vec; +use core::fmt::Debug; + +use crate::read::{ + self, Architecture, Export, FileFlags, Import, NoDynamicRelocationIterator, Object, ObjectKind, + ObjectSection, ReadError, ReadRef, Result, SectionIndex, SymbolIndex, +}; +use crate::{pe, LittleEndian as LE, Pod}; + +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. +pub type CoffBigFile<'data, R = &'data [u8]> = CoffFile<'data, R, pe::AnonObjectHeaderBigobj>; + +/// A COFF object file. +#[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<Self> { + 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, + }) + } +} + +impl<'data, R: ReadRef<'data>, Coff: CoffHeader> read::private::Sealed + for CoffFile<'data, R, Coff> +{ +} + +impl<'data, 'file, R, Coff> Object<'data, 'file> for CoffFile<'data, R, Coff> +where + 'data: 'file, + R: 'file + ReadRef<'data>, + Coff: CoffHeader, +{ + type Segment = CoffSegment<'data, 'file, R, Coff>; + type SegmentIterator = CoffSegmentIterator<'data, 'file, R, Coff>; + type Section = CoffSection<'data, 'file, R, Coff>; + type SectionIterator = CoffSectionIterator<'data, 'file, R, Coff>; + type Comdat = CoffComdat<'data, 'file, R, Coff>; + type ComdatIterator = CoffComdatIterator<'data, 'file, R, Coff>; + type Symbol = CoffSymbol<'data, 'file, R, Coff>; + type SymbolIterator = CoffSymbolIterator<'data, 'file, R, Coff>; + type SymbolTable = CoffSymbolTable<'data, 'file, R, Coff>; + type DynamicRelocationIterator = NoDynamicRelocationIterator; + + fn architecture(&self) -> Architecture { + match self.header.machine() { + pe::IMAGE_FILE_MACHINE_ARMNT => Architecture::Arm, + pe::IMAGE_FILE_MACHINE_ARM64 => Architecture::Aarch64, + pe::IMAGE_FILE_MACHINE_I386 => Architecture::I386, + pe::IMAGE_FILE_MACHINE_AMD64 => Architecture::X86_64, + _ => Architecture::Unknown, + } + } + + #[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(&'file self) -> CoffSegmentIterator<'data, 'file, R, Coff> { + CoffSegmentIterator { + file: self, + iter: self.common.sections.iter(), + } + } + + fn section_by_name_bytes( + &'file self, + section_name: &[u8], + ) -> Option<CoffSection<'data, 'file, R, Coff>> { + self.sections() + .find(|section| section.name_bytes() == Ok(section_name)) + } + + fn section_by_index( + &'file self, + index: SectionIndex, + ) -> Result<CoffSection<'data, 'file, R, Coff>> { + let section = self.common.sections.section(index.0)?; + Ok(CoffSection { + file: self, + index, + section, + }) + } + + fn sections(&'file self) -> CoffSectionIterator<'data, 'file, R, Coff> { + CoffSectionIterator { + file: self, + iter: self.common.sections.iter().enumerate(), + } + } + + fn comdats(&'file self) -> CoffComdatIterator<'data, 'file, R, Coff> { + CoffComdatIterator { + file: self, + index: 0, + } + } + + fn symbol_by_index( + &'file self, + index: SymbolIndex, + ) -> Result<CoffSymbol<'data, 'file, R, Coff>> { + let symbol = self.common.symbols.symbol(index.0)?; + Ok(CoffSymbol { + file: &self.common, + index, + symbol, + }) + } + + fn symbols(&'file self) -> CoffSymbolIterator<'data, 'file, R, Coff> { + CoffSymbolIterator { + file: &self.common, + index: 0, + } + } + + #[inline] + fn symbol_table(&'file self) -> Option<CoffSymbolTable<'data, 'file, R, Coff>> { + Some(CoffSymbolTable { file: &self.common }) + } + + fn dynamic_symbols(&'file self) -> CoffSymbolIterator<'data, 'file, R, Coff> { + CoffSymbolIterator { + file: &self.common, + // Hack: don't return any. + index: self.common.symbols.len(), + } + } + + #[inline] + fn dynamic_symbol_table(&'file self) -> Option<CoffSymbolTable<'data, 'file, R, Coff>> { + None + } + + #[inline] + fn dynamic_relocations(&'file self) -> Option<NoDynamicRelocationIterator> { + None + } + + #[inline] + fn imports(&self) -> Result<Vec<Import<'data>>> { + // TODO: this could return undefined symbols, but not needed yet. + Ok(Vec::new()) + } + + #[inline] + fn exports(&self) -> Result<Vec<Export<'data>>> { + // 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 an anon object header. +/// +/// This can be used to determine the format of the header. +pub fn anon_object_class_id<'data, R: ReadRef<'data>>(data: R) -> Result<pe::ClsId> { + let header = data + .read_at::<pe::AnonObjectHeader>(0) + .read_error("Invalid anon object header size or alignment")?; + Ok(header.class_id) +} + +/// A trait for generic access to `ImageFileHeader` and `AnonObjectHeaderBigobj`. +#[allow(missing_docs)] +pub trait CoffHeader: Debug + Pod { + type ImageSymbol: ImageSymbol; + type ImageSymbolBytes: Debug + Pod; + + /// Return true if this type is `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<'data>> { + 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<'data, R, Self>> { + 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::<pe::ImageFileHeader>(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::<pe::AnonObjectHeaderBigobj>(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) + } +} diff --git a/third_party/rust/object/src/read/coff/import.rs b/third_party/rust/object/src/read/coff/import.rs new file mode 100644 index 0000000000..d635e7592a --- /dev/null +++ b/third_party/rust/object/src/read/coff/import.rs @@ -0,0 +1,209 @@ +//! 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::read::{Architecture, Error, ReadError, ReadRef, Result}; +use crate::{pe, ByteString, Bytes, LittleEndian as LE}; + +/// 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. +#[derive(Debug, Clone)] +pub struct ImportFile<'data> { + header: &'data pe::ImportObjectHeader, + kind: ImportType, + dll: ByteString<'data>, + symbol: ByteString<'data>, + import: Option<ByteString<'data>>, +} + +impl<'data> ImportFile<'data> { + /// Parse it. + pub fn parse<R: ReadRef<'data>>(data: R) -> Result<Self> { + 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 => Architecture::Aarch64, + pe::IMAGE_FILE_MACHINE_I386 => Architecture::I386, + pe::IMAGE_FILE_MACHINE_AMD64 => Architecture::X86_64, + _ => Architecture::Unknown, + } + } + + /// 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::<pe::ImportObjectHeader>(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<ImportObjectData<'data>> { + 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 `ImportObjectHeader`. +#[derive(Debug, Clone)] +pub struct ImportObjectData<'data> { + symbol: ByteString<'data>, + dll: ByteString<'data>, + export: Option<ByteString<'data>>, +} + +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) + } +} diff --git a/third_party/rust/object/src/read/coff/mod.rs b/third_party/rust/object/src/read/coff/mod.rs new file mode 100644 index 0000000000..26020d7974 --- /dev/null +++ b/third_party/rust/object/src/read/coff/mod.rs @@ -0,0 +1,21 @@ +//! Support for reading Windows COFF files. +//! +//! Provides `CoffFile` and related types which implement the `Object` trait. + +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::*; diff --git a/third_party/rust/object/src/read/coff/relocation.rs b/third_party/rust/object/src/read/coff/relocation.rs new file mode 100644 index 0000000000..44d2c68d06 --- /dev/null +++ b/third_party/rust/object/src/read/coff/relocation.rs @@ -0,0 +1,104 @@ +use alloc::fmt; +use core::slice; + +use crate::endian::LittleEndian as LE; +use crate::pe; +use crate::read::{ + ReadRef, Relocation, RelocationEncoding, RelocationKind, RelocationTarget, SymbolIndex, +}; + +use super::{CoffFile, CoffHeader}; + +/// An iterator over the relocations in a `CoffBigSection`. +pub type CoffBigRelocationIterator<'data, 'file, R = &'data [u8]> = + CoffRelocationIterator<'data, 'file, R, pe::AnonObjectHeaderBigobj>; + +/// An iterator over the relocations in a `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<Self::Item> { + self.iter.next().map(|relocation| { + let (kind, size, addend) = match self.file.header.machine() { + pe::IMAGE_FILE_MACHINE_ARMNT => match relocation.typ.get(LE) { + pe::IMAGE_REL_ARM_ADDR32 => (RelocationKind::Absolute, 32, 0), + pe::IMAGE_REL_ARM_ADDR32NB => (RelocationKind::ImageOffset, 32, 0), + pe::IMAGE_REL_ARM_REL32 => (RelocationKind::Relative, 32, -4), + pe::IMAGE_REL_ARM_SECTION => (RelocationKind::SectionIndex, 16, 0), + pe::IMAGE_REL_ARM_SECREL => (RelocationKind::SectionOffset, 32, 0), + typ => (RelocationKind::Coff(typ), 0, 0), + }, + pe::IMAGE_FILE_MACHINE_ARM64 => match relocation.typ.get(LE) { + pe::IMAGE_REL_ARM64_ADDR32 => (RelocationKind::Absolute, 32, 0), + pe::IMAGE_REL_ARM64_ADDR32NB => (RelocationKind::ImageOffset, 32, 0), + pe::IMAGE_REL_ARM64_SECREL => (RelocationKind::SectionOffset, 32, 0), + pe::IMAGE_REL_ARM64_SECTION => (RelocationKind::SectionIndex, 16, 0), + pe::IMAGE_REL_ARM64_ADDR64 => (RelocationKind::Absolute, 64, 0), + pe::IMAGE_REL_ARM64_REL32 => (RelocationKind::Relative, 32, -4), + typ => (RelocationKind::Coff(typ), 0, 0), + }, + pe::IMAGE_FILE_MACHINE_I386 => match relocation.typ.get(LE) { + pe::IMAGE_REL_I386_DIR16 => (RelocationKind::Absolute, 16, 0), + pe::IMAGE_REL_I386_REL16 => (RelocationKind::Relative, 16, 0), + pe::IMAGE_REL_I386_DIR32 => (RelocationKind::Absolute, 32, 0), + pe::IMAGE_REL_I386_DIR32NB => (RelocationKind::ImageOffset, 32, 0), + pe::IMAGE_REL_I386_SECTION => (RelocationKind::SectionIndex, 16, 0), + pe::IMAGE_REL_I386_SECREL => (RelocationKind::SectionOffset, 32, 0), + pe::IMAGE_REL_I386_SECREL7 => (RelocationKind::SectionOffset, 7, 0), + pe::IMAGE_REL_I386_REL32 => (RelocationKind::Relative, 32, -4), + typ => (RelocationKind::Coff(typ), 0, 0), + }, + pe::IMAGE_FILE_MACHINE_AMD64 => match relocation.typ.get(LE) { + pe::IMAGE_REL_AMD64_ADDR64 => (RelocationKind::Absolute, 64, 0), + pe::IMAGE_REL_AMD64_ADDR32 => (RelocationKind::Absolute, 32, 0), + pe::IMAGE_REL_AMD64_ADDR32NB => (RelocationKind::ImageOffset, 32, 0), + pe::IMAGE_REL_AMD64_REL32 => (RelocationKind::Relative, 32, -4), + pe::IMAGE_REL_AMD64_REL32_1 => (RelocationKind::Relative, 32, -5), + pe::IMAGE_REL_AMD64_REL32_2 => (RelocationKind::Relative, 32, -6), + pe::IMAGE_REL_AMD64_REL32_3 => (RelocationKind::Relative, 32, -7), + pe::IMAGE_REL_AMD64_REL32_4 => (RelocationKind::Relative, 32, -8), + pe::IMAGE_REL_AMD64_REL32_5 => (RelocationKind::Relative, 32, -9), + pe::IMAGE_REL_AMD64_SECTION => (RelocationKind::SectionIndex, 16, 0), + pe::IMAGE_REL_AMD64_SECREL => (RelocationKind::SectionOffset, 32, 0), + pe::IMAGE_REL_AMD64_SECREL7 => (RelocationKind::SectionOffset, 7, 0), + typ => (RelocationKind::Coff(typ), 0, 0), + }, + _ => (RelocationKind::Coff(relocation.typ.get(LE)), 0, 0), + }; + let target = RelocationTarget::Symbol(SymbolIndex( + relocation.symbol_table_index.get(LE) as usize, + )); + ( + u64::from(relocation.virtual_address.get(LE)), + Relocation { + kind, + encoding: RelocationEncoding::Generic, + size, + target, + addend, + implicit_addend: true, + }, + ) + }) + } +} + +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() + } +} diff --git a/third_party/rust/object/src/read/coff/section.rs b/third_party/rust/object/src/read/coff/section.rs new file mode 100644 index 0000000000..75804034b8 --- /dev/null +++ b/third_party/rust/object/src/read/coff/section.rs @@ -0,0 +1,574 @@ +use 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, Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags, +}; + +use super::{CoffFile, CoffHeader, CoffRelocationIterator}; + +/// The table of section headers in a COFF or PE file. +#[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<Coff: CoffHeader, R: ReadRef<'data>>( + header: &Coff, + data: R, + offset: u64, + ) -> Result<Self> { + 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: sections indices start at 1. + #[inline] + pub fn iter(&self) -> slice::Iter<'data, pe::ImageSectionHeader> { + 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: usize) -> read::Result<&'data pe::ImageSectionHeader> { + self.sections + .get(index.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<R: ReadRef<'data>>( + &self, + strings: StringTable<'data, R>, + name: &[u8], + ) -> Option<(usize, &'data pe::ImageSectionHeader)> { + self.sections + .iter() + .enumerate() + .find(|(_, section)| section.name(strings) == Ok(name)) + .map(|(index, section)| (index + 1, section)) + } + + /// 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 over the loadable sections of a `CoffBigFile`. +pub type CoffBigSegmentIterator<'data, 'file, R = &'data [u8]> = + CoffSegmentIterator<'data, 'file, R, pe::AnonObjectHeaderBigobj>; + +/// An iterator over the loadable sections of 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::Item> { + self.iter.next().map(|section| CoffSegment { + file: self.file, + section, + }) + } +} + +/// A loadable section of a `CoffBigFile`. +pub type CoffBigSegment<'data, 'file, R = &'data [u8]> = + CoffSegment<'data, 'file, R, pe::AnonObjectHeaderBigobj>; + +/// A loadable section of a `CoffFile`. +#[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> { + 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<Option<&'data [u8]>> { + Ok(read::util::data_range( + self.bytes()?, + self.address(), + address, + size, + )) + } + + #[inline] + fn name_bytes(&self) -> Result<Option<&[u8]>> { + self.section + .name(self.file.common.symbols.strings()) + .map(Some) + } + + #[inline] + fn name(&self) -> Result<Option<&str>> { + 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 over the sections of a `CoffBigFile`. +pub type CoffBigSectionIterator<'data, 'file, R = &'data [u8]> = + CoffSectionIterator<'data, 'file, R, pe::AnonObjectHeaderBigobj>; + +/// An iterator over the sections of 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<slice::Iter<'data, pe::ImageSectionHeader>>, +} + +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::Item> { + self.iter.next().map(|(index, section)| CoffSection { + file: self.file, + index: SectionIndex(index + 1), + section, + }) + } +} + +/// A section of a `CoffBigFile`. +pub type CoffBigSection<'data, 'file, R = &'data [u8]> = + CoffSection<'data, 'file, R, pe::AnonObjectHeaderBigobj>; + +/// A section of a `CoffFile`. +#[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> { + 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<Option<&'data [u8]>> { + Ok(read::util::data_range( + self.bytes()?, + self.address(), + address, + size, + )) + } + + #[inline] + fn compressed_file_range(&self) -> Result<CompressedFileRange> { + Ok(CompressedFileRange::none(self.file_range())) + } + + #[inline] + fn compressed_data(&self) -> Result<CompressedData<'data>> { + self.data().map(CompressedData::none) + } + + #[inline] + fn name_bytes(&self) -> Result<&[u8]> { + self.section.name(self.file.common.symbols.strings()) + } + + #[inline] + fn name(&self) -> Result<&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<Option<&[u8]>> { + Ok(None) + } + + #[inline] + fn segment_name(&self) -> Result<Option<&str>> { + Ok(None) + } + + #[inline] + fn kind(&self) -> SectionKind { + self.section.kind() + } + + fn relocations(&self) -> CoffRelocationIterator<'data, 'file, R, Coff> { + let relocations = self.section.coff_relocations(self.file.data).unwrap_or(&[]); + CoffRelocationIterator { + file: self.file, + iter: relocations.iter(), + } + } + + 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<Option<u32>> { + 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 == core::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::<pe::ImageRelocation>(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::<pe::ImageRelocation>() 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()); + } +} diff --git a/third_party/rust/object/src/read/coff/symbol.rs b/third_party/rust/object/src/read/coff/symbol.rs new file mode 100644 index 0000000000..e95468d7eb --- /dev/null +++ b/third_party/rust/object/src/read/coff/symbol.rs @@ -0,0 +1,626 @@ +use 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. +#[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<Self> { + // 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::<U32Bytes<_>>(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: 0, + } + } + + /// Return the symbol table entry at the given index. + #[inline] + pub fn symbol(&self, index: usize) -> Result<&'data Coff::ImageSymbol> { + self.get::<Coff::ImageSymbol>(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: usize) -> Result<&'data pe::ImageAuxSymbolFunction> { + self.get::<pe::ImageAuxSymbolFunction>(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: usize) -> Result<&'data pe::ImageAuxSymbolSection> { + self.get::<pe::ImageAuxSymbolSection>(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: usize, aux_count: u8) -> Result<&'data [u8]> { + let entries = index + .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<T: Pod>(&self, index: usize, offset: usize) -> Result<&'data T> { + let bytes = index + .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<Entry: SymbolMapEntry, F: Fn(&'data Coff::ImageSymbol) -> Option<Entry>>( + &self, + f: F, + ) -> SymbolMap<Entry> { + 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: usize, +} + +impl<'data, 'table, R: ReadRef<'data>, Coff: CoffHeader> Iterator + for SymbolIterator<'data, 'table, R, Coff> +{ + type Item = (usize, &'data Coff::ImageSymbol); + + fn next(&mut self) -> Option<Self::Item> { + let index = self.index; + let symbol = self.symbols.symbol(index).ok()?; + self.index += 1 + symbol.number_of_aux_symbols() as usize; + Some((index, symbol)) + } +} + +/// A symbol table of a `CoffBigFile`. +pub type CoffBigSymbolTable<'data, 'file, R = &'data [u8]> = + CoffSymbolTable<'data, 'file, R, pe::AnonObjectHeaderBigobj>; + +/// A symbol table of a `CoffFile`. +#[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 { + file: self.file, + index: 0, + } + } + + fn symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol> { + let symbol = self.file.symbols.symbol(index.0)?; + Ok(CoffSymbol { + file: self.file, + index, + symbol, + }) + } +} + +/// An iterator over the symbols of a `CoffBigFile`. +pub type CoffBigSymbolIterator<'data, 'file, R = &'data [u8]> = + CoffSymbolIterator<'data, 'file, R, pe::AnonObjectHeaderBigobj>; + +/// An iterator over the symbols of a `CoffFile`. +pub struct CoffSymbolIterator<'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: usize, +} + +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<Self::Item> { + let index = self.index; + let symbol = self.file.symbols.symbol(index).ok()?; + self.index += 1 + symbol.number_of_aux_symbols() as usize; + Some(CoffSymbol { + file: self.file, + index: SymbolIndex(index), + symbol, + }) + } +} + +/// A symbol of a `CoffBigFile`. +pub type CoffBigSymbol<'data, 'file, R = &'data [u8]> = + CoffSymbol<'data, 'file, R, pe::AnonObjectHeaderBigobj>; + +/// A symbol of a `CoffFile`. +#[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. + pub fn raw_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.0, 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 { + // Only return an address for storage classes that we know use an address. + match self.symbol.storage_class() { + pe::IMAGE_SYM_CLASS_STATIC + | pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL + | pe::IMAGE_SYM_CLASS_LABEL => {} + pe::IMAGE_SYM_CLASS_EXTERNAL => { + if self.symbol.section_number() == pe::IMAGE_SYM_UNDEFINED { + // Undefined or common data, neither of which have an address. + return 0; + } + } + _ => return 0, + } + self.symbol + .address(self.file.image_base, &self.file.sections) + .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.0) { + 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.0) { + 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.value() == 0 && self.symbol.number_of_aux_symbols() > 0 { + 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 + && self.symbol.value() == 0 + { + SymbolSection::Undefined + } else { + SymbolSection::Common + } + } + 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<SectionIndex, SymbolIndex> { + if self.symbol.has_aux_section() { + if let Ok(aux) = self.file.symbols.aux_section(self.index.0) { + 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 `ImageSymbol` and `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. + fn address(&self, image_base: u64, sections: &SectionTable<'_>) -> Result<u64> { + let section_number = self.section_number() as usize; + let section = sections.section(section_number)?; + let virtual_address = u64::from(section.virtual_address.get(LE)); + let value = u64::from(self.value()); + Ok(image_base + virtual_address + value) + } + + /// Return true if the symbol is a definition of a function or data object. + fn is_definition(&self) -> bool { + let section_number = self.section_number(); + if section_number == pe::IMAGE_SYM_UNDEFINED { + return false; + } + match self.storage_class() { + pe::IMAGE_SYM_CLASS_STATIC => { + // Exclude section symbols. + !(self.value() == 0 && self.number_of_aux_symbols() > 0) + } + 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.value() == 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 + } +} diff --git a/third_party/rust/object/src/read/elf/attributes.rs b/third_party/rust/object/src/read/elf/attributes.rs new file mode 100644 index 0000000000..6ec535d724 --- /dev/null +++ b/third_party/rust/object/src/read/elf/attributes.rs @@ -0,0 +1,303 @@ +use 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 subsections. +#[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<Self> { + let mut data = Bytes(data); + + // Skip the version field that is one byte long. + let version = *data + .read::<u8>() + .read_error("Invalid ELF attributes section offset or size")?; + + 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<AttributesSubsectionIterator<'data, Elf>> { + // 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 over the subsections in an ELF attributes section. +#[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<Option<AttributesSubsection<'data, Elf>>> { + if self.data.is_empty() { + return Ok(None); + } + + let result = self.parse(); + if result.is_err() { + self.data = Bytes(&[]); + } + result + } + + fn parse(&mut self) -> Result<Option<AttributesSubsection<'data, Elf>>> { + // First read the subsection length. + let mut data = self.data; + let length = data + .read::<endian::U32Bytes<Elf::Endian>>() + .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")?; + + let vendor = data + .read_string() + .read_error("Invalid ELF attributes vendor")?; + + Ok(Some(AttributesSubsection { + endian: self.endian, + length, + vendor, + data, + })) + } +} + +/// A subsection in an ELF attributes section. +/// +/// A subsection is identified by a vendor name. It contains a series of sub-subsections. +#[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 over the sub-subsections in an ELF attributes section. +#[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<Option<AttributesSubsubsection<'data>>> { + if self.data.is_empty() { + return Ok(None); + } + + let result = self.parse(); + if result.is_err() { + self.data = Bytes(&[]); + } + result + } + + fn parse(&mut self) -> Result<Option<AttributesSubsubsection<'data>>> { + // The format of a sub-section looks like this: + // + // <file-tag> <size> <attribute>* + // | <section-tag> <size> <section-number>* 0 <attribute>* + // | <symbol-tag> <size> <symbol-number>* 0 <attribute>* + let mut data = self.data; + let tag = *data + .read::<u8>() + .read_error("ELF attributes subsection is too short")?; + let length = data + .read::<endian::U32Bytes<Elf::Endian>>() + .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")?; + + 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(Some(AttributesSubsubsection { + tag, + length, + indices, + data, + })) + } +} + +/// A sub-subsection in an ELF attributes section. +/// +/// 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 a sub-subsection in an ELF attributes section. +#[derive(Debug, Clone)] +pub struct AttributeIndexIterator<'data> { + data: Bytes<'data>, +} + +impl<'data> AttributeIndexIterator<'data> { + /// Parse the next index. + pub fn next(&mut self) -> Result<Option<u32>> { + if self.data.is_empty() { + return Ok(None); + } + let err = "Invalid ELF attribute index"; + self.data + .read_uleb128() + .read_error(err)? + .try_into() + .map_err(|_| ()) + .read_error(err) + .map(Some) + } +} + +/// A parser for the attributes in a sub-subsection in an ELF attributes section. +/// +/// 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<Option<u64>> { + 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<u64> { + 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) + } +} diff --git a/third_party/rust/object/src/read/elf/comdat.rs b/third_party/rust/object/src/read/elf/comdat.rs new file mode 100644 index 0000000000..1a2f2f44a9 --- /dev/null +++ b/third_party/rust/object/src/read/elf/comdat.rs @@ -0,0 +1,160 @@ +use 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 over the COMDAT section groups of an `ElfFile32`. +pub type ElfComdatIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfComdatIterator<'data, 'file, elf::FileHeader32<Endian>, R>; +/// An iterator over the COMDAT section groups of an `ElfFile64`. +pub type ElfComdatIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfComdatIterator<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// An iterator over the COMDAT section groups of an `ElfFile`. +#[derive(Debug)] +pub struct ElfComdatIterator<'data, 'file, Elf, R = &'data [u8]> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + pub(super) file: &'file ElfFile<'data, Elf, R>, + pub(super) iter: iter::Enumerate<slice::Iter<'data, Elf::SectionHeader>>, +} + +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<Self::Item> { + 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 of an `ElfFile32`. +pub type ElfComdat32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfComdat<'data, 'file, elf::FileHeader32<Endian>, R>; +/// A COMDAT section group of an `ElfFile64`. +pub type ElfComdat64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfComdat<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// A COMDAT section group of an `ElfFile`. +#[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<Elf::Endian>], +} + +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<ElfComdat<'data, 'file, Elf, R>> { + let (flag, sections) = section.group(file.endian, file.data).ok()??; + if flag != elf::GRP_COMDAT { + return None; + } + Some(ElfComdat { + file, + section, + sections, + }) + } +} + +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<&[u8]> { + // FIXME: check sh_link + let index = self.section.sh_info(self.file.endian) as usize; + let symbol = self.file.symbols.symbol(index)?; + symbol.name(self.file.endian, self.file.symbols.strings()) + } + + fn name(&self) -> read::Result<&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 over the sections in a COMDAT section group of an `ElfFile32`. +pub type ElfComdatSectionIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfComdatSectionIterator<'data, 'file, elf::FileHeader32<Endian>, R>; +/// An iterator over the sections in a COMDAT section group of an `ElfFile64`. +pub type ElfComdatSectionIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfComdatSectionIterator<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// An iterator over the sections in a COMDAT section group of 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<Elf::Endian>>, +} + +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<Self::Item> { + let index = self.sections.next()?; + Some(SectionIndex(index.get(self.file.endian) as usize)) + } +} diff --git a/third_party/rust/object/src/read/elf/compression.rs b/third_party/rust/object/src/read/elf/compression.rs new file mode 100644 index 0000000000..7242dd39c8 --- /dev/null +++ b/third_party/rust/object/src/read/elf/compression.rs @@ -0,0 +1,56 @@ +use core::fmt::Debug; + +use crate::elf; +use crate::endian; +use crate::pod::Pod; + +/// A trait for generic access to `CompressionHeader32` and `CompressionHeader64`. +#[allow(missing_docs)] +pub trait CompressionHeader: Debug + Pod { + type Word: Into<u64>; + 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<Endian: endian::Endian> CompressionHeader for elf::CompressionHeader32<Endian> { + 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<Endian: endian::Endian> CompressionHeader for elf::CompressionHeader64<Endian> { + 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) + } +} diff --git a/third_party/rust/object/src/read/elf/dynamic.rs b/third_party/rust/object/src/read/elf/dynamic.rs new file mode 100644 index 0000000000..5fe15b560d --- /dev/null +++ b/third_party/rust/object/src/read/elf/dynamic.rs @@ -0,0 +1,117 @@ +use 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 `Dyn32` and `Dyn64`. +#[allow(missing_docs)] +pub trait Dyn: Debug + Pod { + type Word: Into<u64>; + 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<u32> { + self.d_tag(endian).into().try_into().ok() + } + + /// Try to convert the value to a `u32`. + fn val32(&self, endian: Self::Endian) -> Option<u32> { + 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<Endian: endian::Endian> Dyn for elf::Dyn32<Endian> { + 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<Endian: endian::Endian> Dyn for elf::Dyn64<Endian> { + 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) + } +} diff --git a/third_party/rust/object/src/read/elf/file.rs b/third_party/rust/object/src/read/elf/file.rs new file mode 100644 index 0000000000..67be37e21f --- /dev/null +++ b/third_party/rust/object/src/read/elf/file.rs @@ -0,0 +1,911 @@ +use alloc::vec::Vec; +use core::convert::TryInto; +use core::fmt::Debug; +use core::mem; + +use crate::read::{ + self, util, Architecture, ByteString, Bytes, Error, Export, FileFlags, Import, Object, + ObjectKind, ReadError, ReadRef, SectionIndex, StringTable, SymbolIndex, +}; +use crate::{elf, endian, Endian, Endianness, Pod, U32}; + +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. +pub type ElfFile32<'data, Endian = Endianness, R = &'data [u8]> = + ElfFile<'data, elf::FileHeader32<Endian>, R>; +/// A 64-bit ELF object file. +pub type ElfFile64<'data, Endian = Endianness, R = &'data [u8]> = + ElfFile<'data, elf::FileHeader64<Endian>, R>; + +/// A partially parsed ELF file. +/// +/// Most of the functionality of this type 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<Self> { + 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. + pub fn raw_header(&self) -> &'data Elf { + self.header + } + + /// Returns the raw ELF segments. + pub fn raw_segments(&self) -> &'data [Elf::ProgramHeader] { + self.segments + } + + fn raw_section_by_name<'file>( + &'file self, + section_name: &[u8], + ) -> Option<ElfSection<'data, 'file, Elf, R>> { + self.sections + .section_by_name(self.endian, section_name) + .map(|(index, section)| ElfSection { + file: self, + index: SectionIndex(index), + section, + }) + } + + #[cfg(feature = "compression")] + fn zdebug_section_by_name<'file>( + &'file self, + section_name: &[u8], + ) -> Option<ElfSection<'data, 'file, Elf, R>> { + 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<ElfSection<'data, 'file, Elf, R>> { + None + } +} + +impl<'data, Elf, R> read::private::Sealed for ElfFile<'data, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Elf, R> Object<'data, 'file> for ElfFile<'data, Elf, R> +where + 'data: 'file, + Elf: FileHeader, + R: 'file + ReadRef<'data>, +{ + type Segment = ElfSegment<'data, 'file, Elf, R>; + type SegmentIterator = ElfSegmentIterator<'data, 'file, Elf, R>; + type Section = ElfSection<'data, 'file, Elf, R>; + type SectionIterator = ElfSectionIterator<'data, 'file, Elf, R>; + type Comdat = ElfComdat<'data, 'file, Elf, R>; + type ComdatIterator = ElfComdatIterator<'data, 'file, Elf, R>; + type Symbol = ElfSymbol<'data, 'file, Elf, R>; + type SymbolIterator = ElfSymbolIterator<'data, 'file, Elf, R>; + type SymbolTable = ElfSymbolTable<'data, 'file, Elf, R>; + type DynamicRelocationIterator = ElfDynamicRelocationIterator<'data, 'file, Elf, R>; + + 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_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_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(&'file self) -> ElfSegmentIterator<'data, 'file, Elf, R> { + ElfSegmentIterator { + file: self, + iter: self.segments.iter(), + } + } + + fn section_by_name_bytes( + &'file self, + section_name: &[u8], + ) -> Option<ElfSection<'data, 'file, Elf, R>> { + self.raw_section_by_name(section_name) + .or_else(|| self.zdebug_section_by_name(section_name)) + } + + fn section_by_index( + &'file self, + index: SectionIndex, + ) -> read::Result<ElfSection<'data, 'file, Elf, R>> { + let section = self.sections.section(index)?; + Ok(ElfSection { + file: self, + index, + section, + }) + } + + fn sections(&'file self) -> ElfSectionIterator<'data, 'file, Elf, R> { + ElfSectionIterator { + file: self, + iter: self.sections.iter().enumerate(), + } + } + + fn comdats(&'file self) -> ElfComdatIterator<'data, 'file, Elf, R> { + ElfComdatIterator { + file: self, + iter: self.sections.iter().enumerate(), + } + } + + fn symbol_by_index( + &'file self, + index: SymbolIndex, + ) -> read::Result<ElfSymbol<'data, 'file, Elf, R>> { + let symbol = self.symbols.symbol(index.0)?; + Ok(ElfSymbol { + endian: self.endian, + symbols: &self.symbols, + index, + symbol, + }) + } + + fn symbols(&'file self) -> ElfSymbolIterator<'data, 'file, Elf, R> { + ElfSymbolIterator { + endian: self.endian, + symbols: &self.symbols, + index: 0, + } + } + + fn symbol_table(&'file self) -> Option<ElfSymbolTable<'data, 'file, Elf, R>> { + if self.symbols.is_empty() { + return None; + } + Some(ElfSymbolTable { + endian: self.endian, + symbols: &self.symbols, + }) + } + + fn dynamic_symbols(&'file self) -> ElfSymbolIterator<'data, 'file, Elf, R> { + ElfSymbolIterator { + endian: self.endian, + symbols: &self.dynamic_symbols, + index: 0, + } + } + + fn dynamic_symbol_table(&'file self) -> Option<ElfSymbolTable<'data, 'file, Elf, R>> { + if self.dynamic_symbols.is_empty() { + return None; + } + Some(ElfSymbolTable { + endian: self.endian, + symbols: &self.dynamic_symbols, + }) + } + + fn dynamic_relocations( + &'file self, + ) -> Option<ElfDynamicRelocationIterator<'data, 'file, Elf, R>> { + Some(ElfDynamicRelocationIterator { + section_index: SectionIndex(1), + file: self, + relocations: None, + }) + } + + /// Get the imported symbols. + fn imports(&self) -> read::Result<Vec<Import<'data>>> { + let mut imports = Vec::new(); + for symbol in self.dynamic_symbols.iter() { + if symbol.is_undefined(self.endian) { + let name = symbol.name(self.endian, self.dynamic_symbols.strings())?; + if !name.is_empty() { + // TODO: use symbol versioning to determine library + imports.push(Import { + name: ByteString(name), + library: ByteString(&[]), + }); + } + } + } + Ok(imports) + } + + /// Get the exported symbols. + fn exports(&self) -> read::Result<Vec<Export<'data>>> { + 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<Option<&'data [u8]>> { + 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<Option<(&'data [u8], u32)>> { + 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::<U32<_>>(crc_offset) + .read_error("Missing ELF .gnu_debuglink crc")? + .get(self.endian); + Ok(Some((filename, crc))) + } + + fn gnu_debugaltlink(&self) -> read::Result<Option<(&'data [u8], &'data [u8])>> { + 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 `FileHeader32` and `FileHeader64`. +#[allow(missing_docs)] +pub trait FileHeader: Debug + Pod { + // Ideally this would be a `u64: From<Word>`, but can't express that. + type Word: Into<u64>; + type Sword: Into<i64>; + type Endian: endian::Endian; + type ProgramHeader: ProgramHeader<Elf = Self, Endian = Self::Endian, Word = Self::Word>; + type SectionHeader: SectionHeader<Elf = Self, Endian = Self::Endian, Word = Self::Word>; + type CompressionHeader: CompressionHeader<Endian = Self::Endian, Word = Self::Word>; + type NoteHeader: NoteHeader<Endian = Self::Endian>; + type Dyn: Dyn<Endian = Self::Endian, Word = Self::Word>; + type Sym: Sym<Endian = Self::Endian, Word = Self::Word>; + type Rel: Rel<Endian = Self::Endian, Word = Self::Word>; + type Rela: Rela<Endian = Self::Endian, Word = Self::Word> + From<Self::Rel>; + + /// 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 `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::<Self>(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> { + 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<Option<&'data Self::SectionHeader>> { + 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::<Self::SectionHeader>() { + // 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<usize> { + 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<usize> { + 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<u32> { + 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::<Self::ProgramHeader>() { + // 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::<Self::SectionHeader>() { + // 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") + } + + /// 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<StringTable<'data, R>> { + if sections.is_empty() { + return Ok(StringTable::default()); + } + let index = self.shstrndx(endian, data)? as usize; + let shstrtab = sections.get(index).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<SectionTable<'data, Self, R>> { + 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<Endian: endian::Endian> FileHeader for elf::FileHeader32<Endian> { + type Word = u32; + type Sword = i32; + type Endian = Endian; + type ProgramHeader = elf::ProgramHeader32<Endian>; + type SectionHeader = elf::SectionHeader32<Endian>; + type CompressionHeader = elf::CompressionHeader32<Endian>; + type NoteHeader = elf::NoteHeader32<Endian>; + type Dyn = elf::Dyn32<Endian>; + type Sym = elf::Sym32<Endian>; + type Rel = elf::Rel32<Endian>; + type Rela = elf::Rela32<Endian>; + + #[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<Endian: endian::Endian> FileHeader for elf::FileHeader64<Endian> { + type Word = u64; + type Sword = i64; + type Endian = Endian; + type ProgramHeader = elf::ProgramHeader64<Endian>; + type SectionHeader = elf::SectionHeader64<Endian>; + type CompressionHeader = elf::CompressionHeader64<Endian>; + type NoteHeader = elf::NoteHeader32<Endian>; + type Dyn = elf::Dyn64<Endian>; + type Sym = elf::Sym64<Endian>; + type Rel = elf::Rel64<Endian>; + type Rela = elf::Rela64<Endian>; + + #[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) + } +} diff --git a/third_party/rust/object/src/read/elf/hash.rs b/third_party/rust/object/src/read/elf/hash.rs new file mode 100644 index 0000000000..aadbb9208a --- /dev/null +++ b/third_party/rust/object/src/read/elf/hash.rs @@ -0,0 +1,220 @@ +use core::mem; + +use crate::elf; +use crate::read::{ReadError, ReadRef, Result}; +use crate::{U32, U64}; + +use super::{FileHeader, Sym, SymbolTable, Version, VersionTable}; + +/// A SysV symbol hash table in an ELF file. +#[derive(Debug)] +pub struct HashTable<'data, Elf: FileHeader> { + buckets: &'data [U32<Elf::Endian>], + chains: &'data [U32<Elf::Endian>], +} + +impl<'data, Elf: FileHeader> HashTable<'data, Elf> { + /// Parse a SysV hash table. + /// + /// `data` should be from a `SHT_HASH` section, or from a + /// segment pointed to via the `DT_HASH` entry. + /// + /// The header is read at offset 0 in the given `data`. + pub fn parse(endian: Elf::Endian, data: &'data [u8]) -> Result<Self> { + let mut offset = 0; + let header = data + .read::<elf::HashHeader<Elf::Endian>>(&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 + } + + /// Use the hash table to find the symbol table entry with the given name, hash and version. + pub fn find<R: ReadRef<'data>>( + &self, + endian: Elf::Endian, + name: &[u8], + hash: u32, + version: Option<&Version<'_>>, + symbols: &SymbolTable<'data, Elf, R>, + versions: &VersionTable<'data, Elf>, + ) -> Option<(usize, &'data Elf::Sym)> { + // Get the chain start from the bucket for this hash. + let mut index = self.buckets[(hash as usize) % self.buckets.len()].get(endian) as usize; + // Avoid infinite loop. + let mut i = 0; + let strings = symbols.strings(); + while index != 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.chains.get(index)?.get(endian) as usize; + i += 1; + } + None + } +} + +/// A GNU symbol hash table in an ELF file. +#[derive(Debug)] +pub struct GnuHashTable<'data, Elf: FileHeader> { + symbol_base: u32, + bloom_shift: u32, + bloom_filters: &'data [u8], + buckets: &'data [U32<Elf::Endian>], + values: &'data [U32<Elf::Endian>], +} + +impl<'data, Elf: FileHeader> GnuHashTable<'data, Elf> { + /// Parse a GNU hash table. + /// + /// `data` should be from a `SHT_GNU_HASH` section, or from a + /// segment pointed to via the `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 `DT_GNU_HASH` entry. + pub fn parse(endian: Elf::Endian, data: &'data [u8]) -> Result<Self> { + let mut offset = 0; + let header = data + .read::<elf::GnuHashHeader<Elf::Endian>>(&mut offset) + .read_error("Invalid GNU hash header")?; + let bloom_len = + u64::from(header.bloom_count.get(endian)) * mem::size_of::<Elf::Word>() 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<u32> { + // 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 + } + + /// Use the hash table to find the symbol table entry with the given name, hash, and version. + pub fn find<R: ReadRef<'data>>( + &self, + endian: Elf::Endian, + name: &[u8], + hash: u32, + version: Option<&Version<'_>>, + symbols: &SymbolTable<'data, Elf, R>, + versions: &VersionTable<'data, Elf>, + ) -> Option<(usize, &'data Elf::Sym)> { + let word_bits = mem::size_of::<Elf::Word>() as u32 * 8; + + // Test against bloom filter. + let bloom_count = self.bloom_filters.len() / mem::size_of::<Elf::Word>(); + let offset = + ((hash / word_bits) & (bloom_count as u32 - 1)) * mem::size_of::<Elf::Word>() as u32; + let filter = if word_bits == 64 { + self.bloom_filters + .read_at::<U64<Elf::Endian>>(offset.into()) + .ok()? + .get(endian) + } else { + self.bloom_filters + .read_at::<U32<Elf::Endian>>(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.buckets[(hash as usize) % self.buckets.len()].get(endian) as usize; + if index == 0 { + return None; + } + + // Test symbols in the chain. + let strings = symbols.strings(); + let symbols = symbols.symbols().get(index..)?; + let values = self + .values + .get(index.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 += 1; + } + None + } +} diff --git a/third_party/rust/object/src/read/elf/mod.rs b/third_party/rust/object/src/read/elf/mod.rs new file mode 100644 index 0000000000..07db6cd660 --- /dev/null +++ b/third_party/rust/object/src/read/elf/mod.rs @@ -0,0 +1,42 @@ +//! Support for reading ELF files. +//! +//! Defines traits to abstract over the difference between ELF32/ELF64, +//! and implements read functionality in terms of these traits. +//! +//! Also provides `ElfFile` and related types which implement the `Object` trait. + +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::*; diff --git a/third_party/rust/object/src/read/elf/note.rs b/third_party/rust/object/src/read/elf/note.rs new file mode 100644 index 0000000000..84d4179de2 --- /dev/null +++ b/third_party/rust/object/src/read/elf/note.rs @@ -0,0 +1,266 @@ +use 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. +#[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<Self> { + 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<Option<Note<'data, Elf>>> { + let mut data = self.data; + if data.is_empty() { + return Ok(None); + } + + let header = data + .read_at::<Elf::NoteHeader>(0) + .read_error("ELF note is too short")?; + + // The name has no alignment requirement. + let offset = mem::size_of::<Elf::NoteHeader>(); + let namesz = header.n_namesz(self.endian) as usize; + let name = 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 = 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 data.skip(offset).is_err() { + data = Bytes(&[]); + } + self.data = data; + + Ok(Some(Note { header, name, desc })) + } +} + +/// 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 `NT_GNU_PROPERTY_TYPE_0`. + pub fn gnu_properties( + &self, + endian: Elf::Endian, + ) -> Option<GnuPropertyIterator<'data, Elf::Endian>> { + 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 `NoteHeader32` and `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<Endian: endian::Endian> NoteHeader for elf::NoteHeader32<Endian> { + 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<Endian: endian::Endian> NoteHeader for elf::NoteHeader64<Endian> { + 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 over the properties in a `NT_GNU_PROPERTY_TYPE_0` note. +#[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<Option<GnuProperty<'data>>> { + let mut data = self.data; + if data.is_empty() { + return Ok(None); + } + + (|| -> Result<_, ()> { + let pr_type = data.read_at::<U32<Endian>>(0)?.get(self.endian); + let pr_datasz = data.read_at::<U32<Endian>>(4)?.get(self.endian) as usize; + let pr_data = data.read_bytes_at(8, pr_datasz)?.0; + data.skip(util::align(8 + pr_datasz, self.align))?; + self.data = data; + Ok(Some(GnuProperty { pr_type, pr_data })) + })() + .read_error("Invalid ELF GNU property") + } +} + +/// A property in a `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<E: endian::Endian>(&self, endian: E) -> read::Result<u32> { + Bytes(self.pr_data) + .read_at::<U32<E>>(0) + .read_error("Invalid ELF GNU property data") + .map(|val| val.get(endian)) + } +} diff --git a/third_party/rust/object/src/read/elf/relocation.rs b/third_party/rust/object/src/read/elf/relocation.rs new file mode 100644 index 0000000000..78032dfdbc --- /dev/null +++ b/third_party/rust/object/src/read/elf/relocation.rs @@ -0,0 +1,576 @@ +use 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, RelocationKind, RelocationTarget, + SectionIndex, SymbolIndex, +}; + +use super::{ElfFile, FileHeader, SectionHeader, SectionTable}; + +/// A mapping from section index to associated relocation sections. +#[derive(Debug)] +pub struct RelocationSections { + relocations: Vec<usize>, +} + +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<Self> { + 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 = SectionIndex(section.sh_link(endian) as usize); + if sh_link != symbol_section { + continue; + } + + let sh_info = section.sh_info(endian) as usize; + if sh_info == 0 { + // Skip dynamic relocations. + continue; + } + if sh_info >= relocations.len() { + return Err(Error("Invalid ELF sh_info for relocation section")); + } + + // Handle multiple relocation sections by chaining them. + let next = relocations[sh_info]; + relocations[sh_info] = 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: usize) -> Option<usize> { + self.relocations.get(index).cloned().filter(|x| *x != 0) + } +} + +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<Self::Item> { + match self { + ElfRelaIterator::Rel(ref mut i) => i.next().cloned().map(Self::Item::from), + ElfRelaIterator::Rela(ref mut i) => i.next().cloned(), + } + } +} + +/// An iterator over the dynamic relocations for an `ElfFile32`. +pub type ElfDynamicRelocationIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfDynamicRelocationIterator<'data, 'file, elf::FileHeader32<Endian>, R>; +/// An iterator over the dynamic relocations for an `ElfFile64`. +pub type ElfDynamicRelocationIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfDynamicRelocationIterator<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// An iterator over the dynamic relocations for 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<ElfRelaIterator<'data, Elf>>, +} + +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<Self::Item> { + 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; + + let sh_link = SectionIndex(section.sh_link(endian) as usize); + if sh_link != 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 over the relocations for an `ElfSection32`. +pub type ElfSectionRelocationIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSectionRelocationIterator<'data, 'file, elf::FileHeader32<Endian>, R>; +/// An iterator over the relocations for an `ElfSection64`. +pub type ElfSectionRelocationIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSectionRelocationIterator<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// An iterator over the relocations for an `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<ElfRelaIterator<'data, Elf>>, +} + +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<Self::Item> { + 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 = SectionIndex(self.file.relocations.get(self.section_index.0)?); + // 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<Elf: FileHeader>( + header: &Elf, + endian: Elf::Endian, + reloc: Elf::Rela, + implicit_addend: bool, +) -> Relocation { + let mut encoding = RelocationEncoding::Generic; + let is_mips64el = header.is_mips64el(endian); + let (kind, size) = match header.e_machine(endian) { + elf::EM_AARCH64 => { + if header.is_type_64() { + match reloc.r_type(endian, false) { + elf::R_AARCH64_ABS64 => (RelocationKind::Absolute, 64), + elf::R_AARCH64_ABS32 => (RelocationKind::Absolute, 32), + elf::R_AARCH64_ABS16 => (RelocationKind::Absolute, 16), + elf::R_AARCH64_PREL64 => (RelocationKind::Relative, 64), + elf::R_AARCH64_PREL32 => (RelocationKind::Relative, 32), + elf::R_AARCH64_PREL16 => (RelocationKind::Relative, 16), + elf::R_AARCH64_CALL26 => { + encoding = RelocationEncoding::AArch64Call; + (RelocationKind::PltRelative, 26) + } + r_type => (RelocationKind::Elf(r_type), 0), + } + } else { + match reloc.r_type(endian, false) { + elf::R_AARCH64_P32_ABS32 => (RelocationKind::Absolute, 32), + r_type => (RelocationKind::Elf(r_type), 0), + } + } + } + elf::EM_ARM => match reloc.r_type(endian, false) { + elf::R_ARM_ABS32 => (RelocationKind::Absolute, 32), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_AVR => match reloc.r_type(endian, false) { + elf::R_AVR_32 => (RelocationKind::Absolute, 32), + elf::R_AVR_16 => (RelocationKind::Absolute, 16), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_BPF => match reloc.r_type(endian, false) { + elf::R_BPF_64_64 => (RelocationKind::Absolute, 64), + elf::R_BPF_64_32 => (RelocationKind::Absolute, 32), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_CSKY => match reloc.r_type(endian, false) { + elf::R_CKCORE_ADDR32 => (RelocationKind::Absolute, 32), + elf::R_CKCORE_PCREL32 => (RelocationKind::Relative, 32), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_386 => match reloc.r_type(endian, false) { + elf::R_386_32 => (RelocationKind::Absolute, 32), + elf::R_386_PC32 => (RelocationKind::Relative, 32), + elf::R_386_GOT32 => (RelocationKind::Got, 32), + elf::R_386_PLT32 => (RelocationKind::PltRelative, 32), + elf::R_386_GOTOFF => (RelocationKind::GotBaseOffset, 32), + elf::R_386_GOTPC => (RelocationKind::GotBaseRelative, 32), + elf::R_386_16 => (RelocationKind::Absolute, 16), + elf::R_386_PC16 => (RelocationKind::Relative, 16), + elf::R_386_8 => (RelocationKind::Absolute, 8), + elf::R_386_PC8 => (RelocationKind::Relative, 8), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_X86_64 => match reloc.r_type(endian, false) { + elf::R_X86_64_64 => (RelocationKind::Absolute, 64), + elf::R_X86_64_PC32 => (RelocationKind::Relative, 32), + elf::R_X86_64_GOT32 => (RelocationKind::Got, 32), + elf::R_X86_64_PLT32 => (RelocationKind::PltRelative, 32), + elf::R_X86_64_GOTPCREL => (RelocationKind::GotRelative, 32), + elf::R_X86_64_32 => (RelocationKind::Absolute, 32), + elf::R_X86_64_32S => { + encoding = RelocationEncoding::X86Signed; + (RelocationKind::Absolute, 32) + } + elf::R_X86_64_16 => (RelocationKind::Absolute, 16), + elf::R_X86_64_PC16 => (RelocationKind::Relative, 16), + elf::R_X86_64_8 => (RelocationKind::Absolute, 8), + elf::R_X86_64_PC8 => (RelocationKind::Relative, 8), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_HEXAGON => match reloc.r_type(endian, false) { + elf::R_HEX_32 => (RelocationKind::Absolute, 32), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_LOONGARCH => match reloc.r_type(endian, false) { + elf::R_LARCH_32 => (RelocationKind::Absolute, 32), + elf::R_LARCH_64 => (RelocationKind::Absolute, 64), + elf::R_LARCH_32_PCREL => (RelocationKind::Relative, 32), + elf::R_LARCH_B16 => { + encoding = RelocationEncoding::LoongArchBranch; + (RelocationKind::Relative, 16) + } + elf::R_LARCH_B21 => { + encoding = RelocationEncoding::LoongArchBranch; + (RelocationKind::Relative, 21) + } + elf::R_LARCH_B26 => { + encoding = RelocationEncoding::LoongArchBranch; + (RelocationKind::Relative, 26) + } + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_MIPS => match reloc.r_type(endian, is_mips64el) { + elf::R_MIPS_16 => (RelocationKind::Absolute, 16), + elf::R_MIPS_32 => (RelocationKind::Absolute, 32), + elf::R_MIPS_64 => (RelocationKind::Absolute, 64), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_MSP430 => match reloc.r_type(endian, false) { + elf::R_MSP430_32 => (RelocationKind::Absolute, 32), + elf::R_MSP430_16_BYTE => (RelocationKind::Absolute, 16), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_PPC => match reloc.r_type(endian, false) { + elf::R_PPC_ADDR32 => (RelocationKind::Absolute, 32), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_PPC64 => match reloc.r_type(endian, false) { + elf::R_PPC64_ADDR32 => (RelocationKind::Absolute, 32), + elf::R_PPC64_ADDR64 => (RelocationKind::Absolute, 64), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_RISCV => match reloc.r_type(endian, false) { + elf::R_RISCV_32 => (RelocationKind::Absolute, 32), + elf::R_RISCV_64 => (RelocationKind::Absolute, 64), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_S390 => match reloc.r_type(endian, false) { + elf::R_390_8 => (RelocationKind::Absolute, 8), + elf::R_390_16 => (RelocationKind::Absolute, 16), + elf::R_390_32 => (RelocationKind::Absolute, 32), + elf::R_390_64 => (RelocationKind::Absolute, 64), + elf::R_390_PC16 => (RelocationKind::Relative, 16), + elf::R_390_PC32 => (RelocationKind::Relative, 32), + elf::R_390_PC64 => (RelocationKind::Relative, 64), + elf::R_390_PC16DBL => { + encoding = RelocationEncoding::S390xDbl; + (RelocationKind::Relative, 16) + } + elf::R_390_PC32DBL => { + encoding = RelocationEncoding::S390xDbl; + (RelocationKind::Relative, 32) + } + elf::R_390_PLT16DBL => { + encoding = RelocationEncoding::S390xDbl; + (RelocationKind::PltRelative, 16) + } + elf::R_390_PLT32DBL => { + encoding = RelocationEncoding::S390xDbl; + (RelocationKind::PltRelative, 32) + } + elf::R_390_GOT16 => (RelocationKind::Got, 16), + elf::R_390_GOT32 => (RelocationKind::Got, 32), + elf::R_390_GOT64 => (RelocationKind::Got, 64), + elf::R_390_GOTENT => { + encoding = RelocationEncoding::S390xDbl; + (RelocationKind::GotRelative, 32) + } + elf::R_390_GOTOFF16 => (RelocationKind::GotBaseOffset, 16), + elf::R_390_GOTOFF32 => (RelocationKind::GotBaseOffset, 32), + elf::R_390_GOTOFF64 => (RelocationKind::GotBaseOffset, 64), + elf::R_390_GOTPC => (RelocationKind::GotBaseRelative, 64), + elf::R_390_GOTPCDBL => { + encoding = RelocationEncoding::S390xDbl; + (RelocationKind::GotBaseRelative, 32) + } + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_SBF => match reloc.r_type(endian, false) { + elf::R_SBF_64_64 => (RelocationKind::Absolute, 64), + elf::R_SBF_64_32 => (RelocationKind::Absolute, 32), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_SPARC | elf::EM_SPARC32PLUS | elf::EM_SPARCV9 => { + match reloc.r_type(endian, false) { + elf::R_SPARC_32 | elf::R_SPARC_UA32 => (RelocationKind::Absolute, 32), + elf::R_SPARC_64 | elf::R_SPARC_UA64 => (RelocationKind::Absolute, 64), + r_type => (RelocationKind::Elf(r_type), 0), + } + } + elf::EM_XTENSA => match reloc.r_type(endian, false) { + elf::R_XTENSA_32 => (RelocationKind::Absolute, 32), + elf::R_XTENSA_32_PCREL => (RelocationKind::Relative, 32), + r_type => (RelocationKind::Elf(r_type), 0), + }, + _ => (RelocationKind::Elf(reloc.r_type(endian, false)), 0), + }; + let sym = reloc.r_sym(endian, is_mips64el) as usize; + let target = if sym == 0 { + RelocationTarget::Absolute + } else { + RelocationTarget::Symbol(SymbolIndex(sym)) + }; + Relocation { + kind, + encoding, + size, + target, + addend: reloc.r_addend(endian).into(), + implicit_addend, + } +} + +/// A trait for generic access to `Rel32` and `Rel64`. +#[allow(missing_docs)] +pub trait Rel: Debug + Pod + Clone { + type Word: Into<u64>; + type Sword: Into<i64>; + 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; +} + +impl<Endian: endian::Endian> Rel for elf::Rel32<Endian> { + 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<Endian: endian::Endian> Rel for elf::Rel64<Endian> { + 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 `Rela32` and `Rela64`. +#[allow(missing_docs)] +pub trait Rela: Debug + Pod + Clone { + type Word: Into<u64>; + type Sword: Into<i64>; + 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; +} + +impl<Endian: endian::Endian> Rela for elf::Rela32<Endian> { + 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<Endian: endian::Endian> Rela for elf::Rela64<Endian> { + 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) + } +} diff --git a/third_party/rust/object/src/read/elf/section.rs b/third_party/rust/object/src/read/elf/section.rs new file mode 100644 index 0000000000..df08f9e3e2 --- /dev/null +++ b/third_party/rust/object/src/read/elf/section.rs @@ -0,0 +1,1146 @@ +use core::fmt::Debug; +use core::{iter, mem, slice, str}; + +use crate::elf; +use crate::endian::{self, Endianness, U32Bytes}; +use crate::pod::Pod; +use crate::read::{ + self, Bytes, CompressedData, CompressedFileRange, CompressionFormat, Error, ObjectSection, + ReadError, ReadRef, 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. +#[derive(Debug, Default, 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>> 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. + #[inline] + pub fn iter(&self) -> slice::Iter<'data, Elf::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. + pub fn section(&self, index: SectionIndex) -> read::Result<&'data Elf::SectionHeader> { + 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<(usize, &'data Elf::SectionHeader)> { + self.sections + .iter() + .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: &'data Elf::SectionHeader, + ) -> read::Result<&'data [u8]> { + section.name(endian, self.strings) + } + + /// Return the string table at the given section index. + /// + /// 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<StringTable<'data, R>> { + 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<SymbolTable<'data, Elf, R>> { + debug_assert!(sh_type == elf::SHT_DYNSYM || sh_type == elf::SHT_SYMTAB); + + let (index, section) = match self + .iter() + .enumerate() + .find(|s| s.1.sh_type(endian) == sh_type) + { + Some(s) => s, + None => return Ok(SymbolTable::default()), + }; + + SymbolTable::parse(endian, data, self, SectionIndex(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<SymbolTable<'data, Elf, R>> { + 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> { + 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<Option<(&'data [Elf::Dyn], SectionIndex)>> { + 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<Option<&'data elf::HashHeader<Elf::Endian>>> { + 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<Option<(HashTable<'data, Elf>, 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<Option<&'data elf::GnuHashHeader<Elf::Endian>>> { + 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<Option<(GnuHashTable<'data, Elf>, 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<Option<(&'data [elf::Versym<Elf::Endian>], 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<Option<(VerdefIterator<'data, Elf>, 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<Option<(VerneedIterator<'data, Elf>, 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<Option<VersionTable<'data, Elf>>> { + 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 over the sections of an `ElfFile32`. +pub type ElfSectionIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSectionIterator<'data, 'file, elf::FileHeader32<Endian>, R>; +/// An iterator over the sections of an `ElfFile64`. +pub type ElfSectionIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSectionIterator<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// An iterator over the sections of an `ElfFile`. +#[derive(Debug)] +pub struct ElfSectionIterator<'data, 'file, Elf, R = &'data [u8]> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + pub(super) file: &'file ElfFile<'data, Elf, R>, + pub(super) iter: iter::Enumerate<slice::Iter<'data, Elf::SectionHeader>>, +} + +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::Item> { + self.iter.next().map(|(index, section)| ElfSection { + index: SectionIndex(index), + file: self.file, + section, + }) + } +} + +/// A section of an `ElfFile32`. +pub type ElfSection32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSection<'data, 'file, elf::FileHeader32<Endian>, R>; +/// A section of an `ElfFile64`. +pub type ElfSection64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSection<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// A section of an `ElfFile`. +#[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> { + 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<Option<CompressedFileRange>> { + 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<Option<CompressedFileRange>> { + let name = match self.name() { + Ok(name) => name, + // I think it's ok to ignore this error? + Err(_) => return Ok(None), + }; + if !name.starts_with(".zdebug_") { + return Ok(None); + } + let (section_offset, section_size) = self + .section + .file_range(self.file.endian) + .read_error("Invalid ELF GNU compressed section type")?; + let mut offset = section_offset; + let data = self.file.data; + // Assume ZLIB-style uncompressed data is no more than 4GB to avoid accidentally + // huge allocations. This also reduces the chance of accidentally matching on a + // .debug_str that happens to start with "ZLIB". + if data + .read_bytes(&mut offset, 8) + .read_error("ELF GNU compressed section is too short")? + != b"ZLIB\0\0\0\0" + { + return Err(Error("Invalid ELF GNU compressed section header")); + } + let uncompressed_size = data + .read::<U32Bytes<_>>(&mut offset) + .read_error("ELF GNU compressed section is too short")? + .get(endian::BigEndian) + .into(); + let compressed_size = section_size + .checked_sub(offset - section_offset) + .read_error("ELF GNU compressed section is too short")?; + Ok(Some(CompressedFileRange { + format: CompressionFormat::Zlib, + offset, + compressed_size, + uncompressed_size, + })) + } +} + +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<Option<&'data [u8]>> { + Ok(read::util::data_range( + self.bytes()?, + self.address(), + address, + size, + )) + } + + fn compressed_file_range(&self) -> read::Result<CompressedFileRange> { + 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<CompressedData<'data>> { + self.compressed_file_range()?.data(self.file.data) + } + + fn name_bytes(&self) -> read::Result<&[u8]> { + self.file + .sections + .section_name(self.file.endian, self.section) + } + + fn name(&self) -> read::Result<&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<Option<&[u8]>> { + Ok(None) + } + + #[inline] + fn segment_name(&self) -> read::Result<Option<&str>> { + 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 flags(&self) -> SectionFlags { + SectionFlags::Elf { + sh_flags: self.section.sh_flags(self.file.endian).into(), + } + } +} + +/// A trait for generic access to `SectionHeader32` and `SectionHeader64`. +#[allow(missing_docs)] +pub trait SectionHeader: Debug + Pod { + type Elf: FileHeader<SectionHeader = Self, Endian = Self::Endian, Word = Self::Word>; + type Word: Into<u64>; + 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") + } + + /// 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]> { + let mut data = self.data(endian, data).map(Bytes)?; + data.read_slice(data.len() / mem::size_of::<T>()) + .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<Option<StringTable<'data, R>>> { + 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<Option<SymbolTable<'data, Self::Elf, R>>> { + 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<Option<(&'data [<Self::Elf as FileHeader>::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")?; + let link = SectionIndex(self.sh_link(endian) as usize); + Ok(Some((rel, link))) + } + + /// 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<Option<(&'data [<Self::Elf as FileHeader>::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")?; + let link = SectionIndex(self.sh_link(endian) as usize); + Ok(Some((rela, link))) + } + + /// 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<Option<(&'data [<Self::Elf as FileHeader>::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")?; + let link = SectionIndex(self.sh_link(endian) as usize); + Ok(Some((dynamic, link))) + } + + /// 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<Option<NoteIterator<'data, Self::Elf>>> { + 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<Option<(u32, &'data [U32Bytes<Self::Endian>])>> { + if self.sh_type(endian) != elf::SHT_GROUP { + return Ok(None); + } + let mut data = self + .data(endian, data) + .read_error("Invalid ELF group section offset or size") + .map(Bytes)?; + let flag = data + .read::<U32Bytes<_>>() + .read_error("Invalid ELF group section offset or size")? + .get(endian); + let count = data.len() / mem::size_of::<U32Bytes<Self::Endian>>(); + let sections = data + .read_slice(count) + .read_error("Invalid ELF group section offset or size")?; + Ok(Some((flag, 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<Option<&'data elf::HashHeader<Self::Endian>>> { + 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::<elf::HashHeader<Self::Endian>>(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<Option<(HashTable<'data, Self::Elf>, 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)?; + let link = SectionIndex(self.sh_link(endian) as usize); + Ok(Some((hash, link))) + } + + /// 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<Option<&'data elf::GnuHashHeader<Self::Endian>>> { + 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::<elf::GnuHashHeader<Self::Endian>>(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<Option<(GnuHashTable<'data, Self::Elf>, 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)?; + let link = SectionIndex(self.sh_link(endian) as usize); + Ok(Some((hash, link))) + } + + /// 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<Option<(&'data [elf::Versym<Self::Endian>], 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")?; + let link = SectionIndex(self.sh_link(endian) as usize); + Ok(Some((versym, link))) + } + + /// 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<Option<(VerdefIterator<'data, Self::Elf>, 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")?; + let link = SectionIndex(self.sh_link(endian) as usize); + Ok(Some((VerdefIterator::new(endian, verdef), link))) + } + + /// 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<Option<(VerneedIterator<'data, Self::Elf>, 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")?; + let link = SectionIndex(self.sh_link(endian) as usize); + Ok(Some((VerneedIterator::new(endian, verneed), link))) + } + + /// 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<Option<AttributesSection<'data, Self::Elf>>> { + 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<AttributesSection<'data, Self::Elf>> { + 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 <Self::Elf as FileHeader>::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::<<Self::Elf as FileHeader>::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<Endian: endian::Endian> SectionHeader for elf::SectionHeader32<Endian> { + type Elf = elf::FileHeader32<Endian>; + 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<Endian: endian::Endian> SectionHeader for elf::SectionHeader64<Endian> { + type Word = u64; + type Endian = Endian; + type Elf = elf::FileHeader64<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) + } +} diff --git a/third_party/rust/object/src/read/elf/segment.rs b/third_party/rust/object/src/read/elf/segment.rs new file mode 100644 index 0000000000..3972731ecf --- /dev/null +++ b/third_party/rust/object/src/read/elf/segment.rs @@ -0,0 +1,332 @@ +use core::fmt::Debug; +use core::{mem, slice, str}; + +use crate::elf; +use crate::endian::{self, Endianness}; +use crate::pod::Pod; +use crate::read::{self, Bytes, ObjectSegment, ReadError, ReadRef, SegmentFlags}; + +use super::{ElfFile, FileHeader, NoteIterator}; + +/// An iterator over the segments of an `ElfFile32`. +pub type ElfSegmentIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSegmentIterator<'data, 'file, elf::FileHeader32<Endian>, R>; +/// An iterator over the segments of an `ElfFile64`. +pub type ElfSegmentIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSegmentIterator<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// An iterator over the segments of 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<Self::Item> { + 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 of an `ElfFile32`. +pub type ElfSegment32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSegment<'data, 'file, elf::FileHeader32<Endian>, R>; +/// A segment of an `ElfFile64`. +pub type ElfSegment64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSegment<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// A segment of an `ElfFile`. +#[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> { + 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<Option<&'data [u8]>> { + Ok(read::util::data_range( + self.bytes()?, + self.address(), + address, + size, + )) + } + + #[inline] + fn name_bytes(&self) -> read::Result<Option<&[u8]>> { + Ok(None) + } + + #[inline] + fn name(&self) -> read::Result<Option<&str>> { + 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 `ProgramHeader32` and `ProgramHeader64`. +#[allow(missing_docs)] +pub trait ProgramHeader: Debug + Pod { + type Elf: FileHeader<ProgramHeader = Self, Endian = Self::Endian, Word = Self::Word>; + type Word: Into<u64>; + 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], ()> { + let mut data = self.data(endian, data).map(Bytes)?; + data.read_slice(data.len() / mem::size_of::<T>()) + } + + /// 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<Option<&'data [u8]>, ()> { + 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<Option<&'data [<Self::Elf as FileHeader>::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 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<Option<NoteIterator<'data, Self::Elf>>> { + 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<Endian: endian::Endian> ProgramHeader for elf::ProgramHeader32<Endian> { + type Word = u32; + type Endian = Endian; + type Elf = elf::FileHeader32<Endian>; + + #[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<Endian: endian::Endian> ProgramHeader for elf::ProgramHeader64<Endian> { + type Word = u64; + type Endian = Endian; + type Elf = elf::FileHeader64<Endian>; + + #[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) + } +} diff --git a/third_party/rust/object/src/read/elf/symbol.rs b/third_party/rust/object/src/read/elf/symbol.rs new file mode 100644 index 0000000000..ee5aa37f19 --- /dev/null +++ b/third_party/rust/object/src/read/elf/symbol.rs @@ -0,0 +1,585 @@ +use alloc::fmt; +use alloc::vec::Vec; +use core::fmt::Debug; +use core::slice; +use core::str; + +use crate::endian::{self, Endianness}; +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 crate::{elf, U32}; + +use super::{FileHeader, SectionHeader, SectionTable}; + +/// A table of symbol entries in an ELF file. +/// +/// Also includes the string table used for the symbol names. +#[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<Elf::Endian>], +} + +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<SymbolTable<'data, Elf, R>> { + 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.iter().enumerate() { + if s.sh_type(endian) == elf::SHT_SYMTAB_SHNDX + && s.sh_link(endian) as usize == section_index.0 + { + shndx_section = SectionIndex(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. + #[inline] + pub fn iter(&self) -> slice::Iter<'data, Elf::Sym> { + 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: usize) -> read::Result<&'data Elf::Sym> { + self.symbols + .get(index) + .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: usize) -> Option<u32> { + self.shndx.get(index).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: &'data Elf::Sym, + index: usize, + ) -> read::Result<Option<SectionIndex>> { + match symbol.st_shndx(endian) { + elf::SHN_UNDEF => Ok(None), + elf::SHN_XINDEX => self + .shndx(endian, index) + .read_error("Missing ELF symbol extended index") + .map(|index| Some(SectionIndex(index 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: &'data 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<Entry: SymbolMapEntry, F: Fn(&'data Elf::Sym) -> Option<Entry>>( + &self, + endian: Elf::Endian, + f: F, + ) -> SymbolMap<Entry> { + 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 of an `ElfFile32`. +pub type ElfSymbolTable32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbolTable<'data, 'file, elf::FileHeader32<Endian>, R>; +/// A symbol table of an `ElfFile32`. +pub type ElfSymbolTable64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbolTable<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// A symbol table of an `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 { + endian: self.endian, + symbols: self.symbols, + index: 0, + } + } + + fn symbol_by_index(&self, index: SymbolIndex) -> read::Result<Self::Symbol> { + let symbol = self.symbols.symbol(index.0)?; + Ok(ElfSymbol { + endian: self.endian, + symbols: self.symbols, + index, + symbol, + }) + } +} + +/// An iterator over the symbols of an `ElfFile32`. +pub type ElfSymbolIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbolIterator<'data, 'file, elf::FileHeader32<Endian>, R>; +/// An iterator over the symbols of an `ElfFile64`. +pub type ElfSymbolIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbolIterator<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// An iterator over the symbols of an `ElfFile`. +pub struct ElfSymbolIterator<'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: usize, +} + +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<Self::Item> { + let index = self.index; + let symbol = self.symbols.symbols.get(index)?; + self.index += 1; + Some(ElfSymbol { + endian: self.endian, + symbols: self.symbols, + index: SymbolIndex(index), + symbol, + }) + } +} + +/// A symbol of an `ElfFile32`. +pub type ElfSymbol32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbol<'data, 'file, elf::FileHeader32<Endian>, R>; +/// A symbol of an `ElfFile64`. +pub type ElfSymbol64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbol<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// A symbol of an `ElfFile`. +#[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> { + /// Return a reference to the raw symbol structure. + #[inline] + pub fn raw_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 if self.index.0 == 0 => SymbolKind::Null, + elf::STT_NOTYPE => SymbolKind::Label, + 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.0) { + 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.st_shndx(self.endian) == elf::SHN_UNDEF + } + + #[inline] + fn is_definition(&self) -> bool { + self.symbol.is_definition(self.endian) + } + + #[inline] + fn is_common(&self) -> bool { + self.symbol.st_shndx(self.endian) == elf::SHN_COMMON + } + + #[inline] + fn is_weak(&self) -> bool { + self.symbol.st_bind() == elf::STB_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.st_bind() != elf::STB_LOCAL + } + + #[inline] + fn is_local(&self) -> bool { + self.symbol.st_bind() == elf::STB_LOCAL + } + + #[inline] + fn flags(&self) -> SymbolFlags<SectionIndex, SymbolIndex> { + SymbolFlags::Elf { + st_info: self.symbol.st_info(), + st_other: self.symbol.st_other(), + } + } +} + +/// A trait for generic access to `Sym32` and `Sym64`. +#[allow(missing_docs)] +pub trait Sym: Debug + Pod { + type Word: Into<u64>; + 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 is undefined. + #[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 st_type = self.st_type(); + (st_type == elf::STT_NOTYPE || st_type == elf::STT_FUNC || st_type == elf::STT_OBJECT) + && self.st_shndx(endian) != elf::SHN_UNDEF + } +} + +impl<Endian: endian::Endian> Sym for elf::Sym32<Endian> { + 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<Endian: endian::Endian> Sym for elf::Sym64<Endian> { + 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) + } +} diff --git a/third_party/rust/object/src/read/elf/version.rs b/third_party/rust/object/src/read/elf/version.rs new file mode 100644 index 0000000000..cc87bbef1c --- /dev/null +++ b/third_party/rust/object/src/read/elf/version.rs @@ -0,0 +1,421 @@ +use alloc::vec::Vec; + +use crate::read::{Bytes, ReadError, ReadRef, Result, StringTable}; +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 `SHT_GNU_verdef` and `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, +} + +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 + } +} + +/// 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 `SHT_GNU_versym`, `SHT_GNU_verdef` and `SHT_GNU_verneed` sections. +#[derive(Debug, Clone)] +pub struct VersionTable<'data, Elf: FileHeader> { + symbols: &'data [elf::Versym<Elf::Endian>], + versions: Vec<Version<'data>>, +} + +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<R: ReadRef<'data>>( + endian: Elf::Endian, + versyms: &'data [elf::Versym<Elf::Endian>], + verdefs: Option<VerdefIterator<'data, Elf>>, + verneeds: Option<VerneedIterator<'data, Elf>>, + strings: StringTable<'data, R>, + ) -> Result<Self> { + 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::<Version>() 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, + }; + } + } + } + if let Some(mut verneeds) = verneeds { + while let Some((_, 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, + }; + } + } + } + + 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: usize) -> VersionIndex { + let version_index = match self.symbols.get(index) { + 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<Option<&Version<'data>>> { + 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: usize, 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 over the entries in an 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<Option<(&'data elf::Verdef<Elf::Endian>, VerdauxIterator<'data, Elf>)>> { + if self.data.is_empty() { + return Ok(None); + } + + let verdef = self + .data + .read_at::<elf::Verdef<_>>(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(Some((verdef, verdaux))) + } +} + +/// An iterator over the auxiliary records for an entry in an 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<Option<&'data elf::Verdaux<Elf::Endian>>> { + if self.count == 0 { + return Ok(None); + } + + let verdaux = self + .data + .read_at::<elf::Verdaux<_>>(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")?; + self.count -= 1; + Ok(Some(verdaux)) + } +} + +/// An iterator over the entries in an 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<Elf::Endian>, + VernauxIterator<'data, Elf>, + )>, + > { + if self.data.is_empty() { + return Ok(None); + } + + let verneed = self + .data + .read_at::<elf::Verneed<_>>(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(Some((verneed, vernaux))) + } +} + +/// An iterator over the auxiliary records for an entry in an 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<Option<&'data elf::Vernaux<Elf::Endian>>> { + if self.count == 0 { + return Ok(None); + } + + let vernaux = self + .data + .read_at::<elf::Vernaux<_>>(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")?; + self.count -= 1; + Ok(Some(vernaux)) + } +} + +impl<Endian: endian::Endian> elf::Verdaux<Endian> { + /// 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<Endian: endian::Endian> elf::Verneed<Endian> { + /// 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<Endian: endian::Endian> elf::Vernaux<Endian> { + /// 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") + } +} diff --git a/third_party/rust/object/src/read/macho/dyld_cache.rs b/third_party/rust/object/src/read/macho/dyld_cache.rs new file mode 100644 index 0000000000..68f27f5491 --- /dev/null +++ b/third_party/rust/object/src/read/macho/dyld_cache.rs @@ -0,0 +1,343 @@ +use alloc::vec::Vec; +use core::slice; + +use crate::read::{Error, File, ReadError, ReadRef, Result}; +use crate::{macho, Architecture, Endian, Endianness}; + +/// 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<DyldSubCache<'data, E, R>>, + mappings: &'data [macho::DyldCacheMappingInfo<E>], + images: &'data [macho::DyldCacheImageInfo<E>], + 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<E>], +} + +// This is the offset of the images_across_all_subcaches_count field. +const MIN_HEADER_SIZE_SUBCACHES: u32 = 0x1c4; + +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<Self> { + 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)?.unwrap_or(&[]); + + if subcache_data.len() != subcaches_info.len() + 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 (.1, .2, ...), if present. + let mut subcaches = Vec::new(); + for (&data, info) in subcache_data.iter().zip(subcaches_info.iter()) { + let sc_header = macho::DyldCacheHeader::<E>::parse(data)?; + if sc_header.uuid != info.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::<E>::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<E>>, +} + +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<DyldCacheImage<'data, 'cache, E, R>> { + 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<E>, +} + +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<'data, R>> { + File::parse_dyld_cache_image(self) + } +} + +impl<E: Endian> macho::DyldCacheHeader<E> { + /// Read the dyld cache header. + pub fn parse<'data, R: ReadRef<'data>>(data: R) -> Result<&'data Self> { + data.read_at::<macho::DyldCacheHeader<E>>(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<E>]> { + data.read_slice_at::<macho::DyldCacheMappingInfo<E>>( + 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. + pub fn subcaches<'data, R: ReadRef<'data>>( + &self, + endian: E, + data: R, + ) -> Result<Option<&'data [macho::DyldSubCacheInfo<E>]>> { + if self.mapping_offset.get(endian) >= MIN_HEADER_SIZE_SUBCACHES { + let subcaches = data + .read_slice_at::<macho::DyldSubCacheInfo<E>>( + self.subcaches_offset.get(endian).into(), + self.subcaches_count.get(endian) as usize, + ) + .read_error("Invalid dyld subcaches size or alignment")?; + Ok(Some(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 { + 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<E>]> { + if self.mapping_offset.get(endian) >= MIN_HEADER_SIZE_SUBCACHES { + data.read_slice_at::<macho::DyldCacheImageInfo<E>>( + 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::<macho::DyldCacheImageInfo<E>>( + self.images_offset.get(endian).into(), + self.images_count.get(endian) as usize, + ) + .read_error("Invalid dyld cache image size or alignment") + } + } +} + +impl<E: Endian> macho::DyldCacheImageInfo<E> { + /// 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<E>], + ) -> Result<u64> { + 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<E: Endian>( + address: u64, + endian: E, + mappings: &[macho::DyldCacheMappingInfo<E>], +) -> Option<u64> { + 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 +} diff --git a/third_party/rust/object/src/read/macho/fat.rs b/third_party/rust/object/src/read/macho/fat.rs new file mode 100644 index 0000000000..d4301b7e11 --- /dev/null +++ b/third_party/rust/object/src/read/macho/fat.rs @@ -0,0 +1,122 @@ +use crate::read::{Architecture, Error, ReadError, ReadRef, Result}; +use crate::{macho, BigEndian, Pod}; + +pub use macho::{FatArch32, FatArch64, FatHeader}; + +impl FatHeader { + /// Attempt to parse a fat header. + /// + /// Does not validate the magic value. + pub fn parse<'data, R: ReadRef<'data>>(file: R) -> Result<&'data FatHeader> { + file.read_at::<FatHeader>(0) + .read_error("Invalid fat header size or alignment") + } + + /// Attempt to parse a fat header and 32-bit fat arches. + pub fn parse_arch32<'data, R: ReadRef<'data>>(file: R) -> Result<&'data [FatArch32]> { + let mut offset = 0; + let header = file + .read::<FatHeader>(&mut offset) + .read_error("Invalid fat header size or alignment")?; + if header.magic.get(BigEndian) != macho::FAT_MAGIC { + return Err(Error("Invalid 32-bit fat magic")); + } + file.read_slice::<FatArch32>(&mut offset, header.nfat_arch.get(BigEndian) as usize) + .read_error("Invalid nfat_arch") + } + + /// Attempt to parse a fat header and 64-bit fat arches. + pub fn parse_arch64<'data, R: ReadRef<'data>>(file: R) -> Result<&'data [FatArch64]> { + let mut offset = 0; + let header = file + .read::<FatHeader>(&mut offset) + .read_error("Invalid fat header size or alignment")?; + if header.magic.get(BigEndian) != macho::FAT_MAGIC_64 { + return Err(Error("Invalid 64-bit fat magic")); + } + file.read_slice::<FatArch64>(&mut offset, header.nfat_arch.get(BigEndian) as usize) + .read_error("Invalid nfat_arch") + } +} + +/// A trait for generic access to `FatArch32` and `FatArch64`. +#[allow(missing_docs)] +pub trait FatArch: Pod { + type Word: Into<u64>; + + 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; + + 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; + + 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) + } +} diff --git a/third_party/rust/object/src/read/macho/file.rs b/third_party/rust/object/src/read/macho/file.rs new file mode 100644 index 0000000000..368c28bbd2 --- /dev/null +++ b/third_party/rust/object/src/read/macho/file.rs @@ -0,0 +1,731 @@ +use alloc::vec::Vec; +use core::fmt::Debug; +use core::{mem, str}; + +use crate::read::{ + self, Architecture, ComdatKind, Error, Export, FileFlags, Import, NoDynamicRelocationIterator, + Object, ObjectComdat, ObjectKind, ObjectMap, ObjectSection, ReadError, ReadRef, Result, + SectionIndex, SymbolIndex, +}; +use crate::{endian, macho, BigEndian, ByteString, Endian, Endianness, Pod}; + +use super::{ + DyldCacheImage, LoadCommandIterator, MachOSection, MachOSectionInternal, MachOSectionIterator, + MachOSegment, MachOSegmentInternal, MachOSegmentIterator, MachOSymbol, MachOSymbolIterator, + MachOSymbolTable, Nlist, Section, Segment, SymbolTable, +}; + +/// A 32-bit Mach-O object file. +pub type MachOFile32<'data, Endian = Endianness, R = &'data [u8]> = + MachOFile<'data, macho::MachHeader32<Endian>, R>; +/// A 64-bit Mach-O object file. +pub type MachOFile64<'data, Endian = Endianness, R = &'data [u8]> = + MachOFile<'data, macho::MachHeader64<Endian>, 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<MachOSegmentInternal<'data, Mach, R>>, + pub(super) sections: Vec<MachOSectionInternal<'data, Mach>>, + 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<Self> { + 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)? { + let segment_index = segments.len(); + segments.push(MachOSegmentInternal { segment, data }); + for section in segment.sections(endian, section_data)? { + let index = SectionIndex(sections.len() + 1); + sections.push(MachOSectionInternal::parse(index, segment_index, section)); + } + } 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<Self> { + 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<R> = 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. + 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); + } + let segment_index = segments.len(); + segments.push(MachOSegmentInternal { segment, data }); + + for section in segment.sections(endian, section_data)? { + let index = SectionIndex(sections.len() + 1); + sections.push(MachOSectionInternal::parse(index, segment_index, section)); + } + } 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>> { + index + .0 + .checked_sub(1) + .and_then(|index| self.sections.get(index)) + .read_error("Invalid Mach-O section index") + } + + pub(super) fn segment_internal( + &self, + index: usize, + ) -> Result<&MachOSegmentInternal<'data, Mach, R>> { + self.segments + .get(index) + .read_error("Invalid Mach-O segment index") + } +} + +impl<'data, Mach, R> read::private::Sealed for MachOFile<'data, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Mach, R> Object<'data, 'file> for MachOFile<'data, Mach, R> +where + 'data: 'file, + Mach: MachHeader, + R: 'file + ReadRef<'data>, +{ + type Segment = MachOSegment<'data, 'file, Mach, R>; + type SegmentIterator = MachOSegmentIterator<'data, 'file, Mach, R>; + type Section = MachOSection<'data, 'file, Mach, R>; + type SectionIterator = MachOSectionIterator<'data, 'file, Mach, R>; + type Comdat = MachOComdat<'data, 'file, Mach, R>; + type ComdatIterator = MachOComdatIterator<'data, 'file, Mach, R>; + type Symbol = MachOSymbol<'data, 'file, Mach, R>; + type SymbolIterator = MachOSymbolIterator<'data, 'file, Mach, R>; + type SymbolTable = MachOSymbolTable<'data, 'file, Mach, R>; + type DynamicRelocationIterator = NoDynamicRelocationIterator; + + 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, + } + } + + #[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(&'file self) -> MachOSegmentIterator<'data, 'file, Mach, R> { + MachOSegmentIterator { + file: self, + iter: self.segments.iter(), + } + } + + fn section_by_name_bytes( + &'file self, + section_name: &[u8], + ) -> Option<MachOSection<'data, 'file, Mach, R>> { + // Translate the "." prefix to the "__" prefix used by OSX/Mach-O, eg + // ".debug_info" to "__debug_info", and limit to 16 bytes total. + let system_name = if section_name.starts_with(b".") { + if section_name.len() > 15 { + Some(§ion_name[1..15]) + } else { + Some(§ion_name[1..]) + } + } else { + None + }; + let cmp_section_name = |section: &MachOSection<'data, 'file, Mach, R>| { + section + .name_bytes() + .map(|name| { + section_name == name + || system_name + .filter(|system_name| { + name.starts_with(b"__") && name[2..] == **system_name + }) + .is_some() + }) + .unwrap_or(false) + }; + + self.sections().find(cmp_section_name) + } + + fn section_by_index( + &'file self, + index: SectionIndex, + ) -> Result<MachOSection<'data, 'file, Mach, R>> { + let internal = *self.section_internal(index)?; + Ok(MachOSection { + file: self, + internal, + }) + } + + fn sections(&'file self) -> MachOSectionIterator<'data, 'file, Mach, R> { + MachOSectionIterator { + file: self, + iter: self.sections.iter(), + } + } + + fn comdats(&'file self) -> MachOComdatIterator<'data, 'file, Mach, R> { + MachOComdatIterator { file: self } + } + + fn symbol_by_index( + &'file self, + index: SymbolIndex, + ) -> Result<MachOSymbol<'data, 'file, Mach, R>> { + let nlist = self.symbols.symbol(index.0)?; + MachOSymbol::new(self, index, nlist).read_error("Unsupported Mach-O symbol index") + } + + fn symbols(&'file self) -> MachOSymbolIterator<'data, 'file, Mach, R> { + MachOSymbolIterator { + file: self, + index: 0, + } + } + + #[inline] + fn symbol_table(&'file self) -> Option<MachOSymbolTable<'data, 'file, Mach, R>> { + Some(MachOSymbolTable { file: self }) + } + + fn dynamic_symbols(&'file self) -> MachOSymbolIterator<'data, 'file, Mach, R> { + MachOSymbolIterator { + file: self, + index: self.symbols.len(), + } + } + + #[inline] + fn dynamic_symbol_table(&'file self) -> Option<MachOSymbolTable<'data, 'file, Mach, R>> { + None + } + + fn object_map(&'file self) -> ObjectMap<'data> { + self.symbols.object_map(self.endian) + } + + fn imports(&self) -> Result<Vec<Import<'data>>> { + 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(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<Vec<Export<'data>>> { + 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(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(&'file self) -> Option<NoDynamicRelocationIterator> { + None + } + + fn has_debug_symbols(&self) -> bool { + self.section_by_name(".debug_info").is_some() + } + + fn mach_uuid(&self) -> Result<Option<[u8; 16]>> { + 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 over the COMDAT section groups of a `MachOFile64`. +pub type MachOComdatIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOComdatIterator<'data, 'file, macho::MachHeader32<Endian>, R>; +/// An iterator over the COMDAT section groups of a `MachOFile64`. +pub type MachOComdatIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOComdatIterator<'data, 'file, macho::MachHeader64<Endian>, R>; + +/// An iterator over the COMDAT section groups of a `MachOFile`. +#[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<Self::Item> { + None + } +} + +/// A COMDAT section group of a `MachOFile32`. +pub type MachOComdat32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOComdat<'data, 'file, macho::MachHeader32<Endian>, R>; + +/// A COMDAT section group of a `MachOFile64`. +pub type MachOComdat64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOComdat<'data, 'file, macho::MachHeader64<Endian>, R>; + +/// A COMDAT section group of a `MachOFile`. +#[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<&[u8]> { + unreachable!(); + } + + #[inline] + fn name(&self) -> Result<&str> { + unreachable!(); + } + + #[inline] + fn sections(&self) -> Self::SectionIterator { + unreachable!(); + } +} + +/// An iterator over the sections in a COMDAT section group of a `MachOFile32`. +pub type MachOComdatSectionIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOComdatSectionIterator<'data, 'file, macho::MachHeader32<Endian>, R>; +/// An iterator over the sections in a COMDAT section group of a `MachOFile64`. +pub type MachOComdatSectionIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOComdatSectionIterator<'data, 'file, macho::MachHeader64<Endian>, R>; + +/// An iterator over the sections in a COMDAT section group of a `MachOFile`. +#[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<Self::Item> { + None + } +} + +/// A trait for generic access to `MachHeader32` and `MachHeader64`. +#[allow(missing_docs)] +pub trait MachHeader: Debug + Pod { + type Word: Into<u64>; + type Endian: endian::Endian; + type Segment: Segment<Endian = Self::Endian, Section = Self::Section>; + type Section: Section<Endian = Self::Endian>; + type Nlist: Nlist<Endian = Self::Endian>; + + /// 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::<Self>(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> { + 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<LoadCommandIterator<'data, Self::Endian>> { + let data = data + .read_bytes_at( + header_offset + mem::size_of::<Self>() 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<Option<[u8; 16]>> { + 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<Endian: endian::Endian> MachHeader for macho::MachHeader32<Endian> { + type Word = u32; + type Endian = Endian; + type Segment = macho::SegmentCommand32<Endian>; + type Section = macho::Section32<Endian>; + type Nlist = macho::Nlist32<Endian>; + + 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<Endian: endian::Endian> MachHeader for macho::MachHeader64<Endian> { + type Word = u64; + type Endian = Endian; + type Segment = macho::SegmentCommand64<Endian>; + type Section = macho::Section64<Endian>; + type Nlist = macho::Nlist64<Endian>; + + 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) + } +} diff --git a/third_party/rust/object/src/read/macho/load_command.rs b/third_party/rust/object/src/read/macho/load_command.rs new file mode 100644 index 0000000000..e9af89d8bf --- /dev/null +++ b/third_party/rust/object/src/read/macho/load_command.rs @@ -0,0 +1,373 @@ +use 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 over the load commands of 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<Option<LoadCommandData<'data, E>>> { + if self.ncmds == 0 { + return Ok(None); + } + let header = self + .data + .read_at::<macho::LoadCommand<E>>(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::<macho::LoadCommand<E>>() { + return Err(Error("Invalid Mach-O load command size")); + } + let data = self + .data + .read_bytes(cmdsize) + .read_error("Invalid Mach-O load command size")?; + self.ncmds -= 1; + Ok(Some(LoadCommandData { + cmd, + data, + marker: Default::default(), + })) + } +} + +/// The data for a `LoadCommand`. +#[derive(Debug, Clone, Copy)] +pub struct LoadCommandData<'data, E: Endian> { + cmd: u32, + // Includes the header. + data: Bytes<'data>, + marker: PhantomData<E>, +} + +impl<'data, E: Endian> LoadCommandData<'data, E> { + /// Return the `cmd` field of the `LoadCommand`. + /// + /// This is one of the `LC_` constants. + pub fn cmd(&self) -> u32 { + self.cmd + } + + /// Return the `cmdsize` field of the `LoadCommand`. + pub fn cmdsize(&self) -> u32 { + self.data.len() as u32 + } + + /// Parse the data as the given type. + #[inline] + pub fn data<T: Pod>(&self) -> Result<&'data T> { + self.data + .read_at(0) + .read_error("Invalid Mach-O command size") + } + + /// Raw bytes of this 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<E>) -> 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<LoadCommandVariant<'data, E>> { + 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 `SegmentCommand32`. + /// + /// Returns the segment command and the data containing the sections. + pub fn segment_32(self) -> Result<Option<(&'data macho::SegmentCommand32<E>, &'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 `SymtabCommand`. + /// + /// Returns the segment command and the data containing the sections. + pub fn symtab(self) -> Result<Option<&'data macho::SymtabCommand<E>>> { + if self.cmd == macho::LC_SYMTAB { + Some(self.data()).transpose() + } else { + Ok(None) + } + } + + /// Try to parse this command as a `DysymtabCommand`. + pub fn dysymtab(self) -> Result<Option<&'data macho::DysymtabCommand<E>>> { + if self.cmd == macho::LC_DYSYMTAB { + Some(self.data()).transpose() + } else { + Ok(None) + } + } + + /// Try to parse this command as a `DylibCommand`. + pub fn dylib(self) -> Result<Option<&'data macho::DylibCommand<E>>> { + 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 `UuidCommand`. + pub fn uuid(self) -> Result<Option<&'data macho::UuidCommand<E>>> { + if self.cmd == macho::LC_UUID { + Some(self.data()).transpose() + } else { + Ok(None) + } + } + + /// Try to parse this command as a `SegmentCommand64`. + pub fn segment_64(self) -> Result<Option<(&'data macho::SegmentCommand64<E>, &'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 `DyldInfoCommand`. + pub fn dyld_info(self) -> Result<Option<&'data macho::DyldInfoCommand<E>>> { + 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 `EntryPointCommand`. + pub fn entry_point(self) -> Result<Option<&'data macho::EntryPointCommand<E>>> { + if self.cmd == macho::LC_MAIN { + Some(self.data()).transpose() + } else { + Ok(None) + } + } +} + +/// A `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<E>, &'data [u8]), + /// `LC_SYMTAB` + Symtab(&'data macho::SymtabCommand<E>), + // obsolete: `LC_SYMSEG` + //Symseg(&'data macho::SymsegCommand<E>), + /// `LC_THREAD` or `LC_UNIXTHREAD` + Thread(&'data macho::ThreadCommand<E>, &'data [u8]), + // obsolete: `LC_IDFVMLIB` or `LC_LOADFVMLIB` + //Fvmlib(&'data macho::FvmlibCommand<E>), + // obsolete: `LC_IDENT` + //Ident(&'data macho::IdentCommand<E>), + // internal: `LC_FVMFILE` + //Fvmfile(&'data macho::FvmfileCommand<E>), + // internal: `LC_PREPAGE` + /// `LC_DYSYMTAB` + Dysymtab(&'data macho::DysymtabCommand<E>), + /// `LC_LOAD_DYLIB`, `LC_LOAD_WEAK_DYLIB`, `LC_REEXPORT_DYLIB`, + /// `LC_LAZY_LOAD_DYLIB`, or `LC_LOAD_UPWARD_DYLIB` + Dylib(&'data macho::DylibCommand<E>), + /// `LC_ID_DYLIB` + IdDylib(&'data macho::DylibCommand<E>), + /// `LC_LOAD_DYLINKER` + LoadDylinker(&'data macho::DylinkerCommand<E>), + /// `LC_ID_DYLINKER` + IdDylinker(&'data macho::DylinkerCommand<E>), + /// `LC_PREBOUND_DYLIB` + PreboundDylib(&'data macho::PreboundDylibCommand<E>), + /// `LC_ROUTINES` + Routines32(&'data macho::RoutinesCommand32<E>), + /// `LC_SUB_FRAMEWORK` + SubFramework(&'data macho::SubFrameworkCommand<E>), + /// `LC_SUB_UMBRELLA` + SubUmbrella(&'data macho::SubUmbrellaCommand<E>), + /// `LC_SUB_CLIENT` + SubClient(&'data macho::SubClientCommand<E>), + /// `LC_SUB_LIBRARY` + SubLibrary(&'data macho::SubLibraryCommand<E>), + /// `LC_TWOLEVEL_HINTS` + TwolevelHints(&'data macho::TwolevelHintsCommand<E>), + /// `LC_PREBIND_CKSUM` + PrebindCksum(&'data macho::PrebindCksumCommand<E>), + /// `LC_SEGMENT_64` + Segment64(&'data macho::SegmentCommand64<E>, &'data [u8]), + /// `LC_ROUTINES_64` + Routines64(&'data macho::RoutinesCommand64<E>), + /// `LC_UUID` + Uuid(&'data macho::UuidCommand<E>), + /// `LC_RPATH` + Rpath(&'data macho::RpathCommand<E>), + /// `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<E>), + /// `LC_ENCRYPTION_INFO` + EncryptionInfo32(&'data macho::EncryptionInfoCommand32<E>), + /// `LC_DYLD_INFO` or `LC_DYLD_INFO_ONLY` + DyldInfo(&'data macho::DyldInfoCommand<E>), + /// `LC_VERSION_MIN_MACOSX`, `LC_VERSION_MIN_IPHONEOS`, `LC_VERSION_MIN_WATCHOS`, + /// or `LC_VERSION_MIN_TVOS` + VersionMin(&'data macho::VersionMinCommand<E>), + /// `LC_DYLD_ENVIRONMENT` + DyldEnvironment(&'data macho::DylinkerCommand<E>), + /// `LC_MAIN` + EntryPoint(&'data macho::EntryPointCommand<E>), + /// `LC_SOURCE_VERSION` + SourceVersion(&'data macho::SourceVersionCommand<E>), + /// `LC_ENCRYPTION_INFO_64` + EncryptionInfo64(&'data macho::EncryptionInfoCommand64<E>), + /// `LC_LINKER_OPTION` + LinkerOption(&'data macho::LinkerOptionCommand<E>), + /// `LC_NOTE` + Note(&'data macho::NoteCommand<E>), + /// `LC_BUILD_VERSION` + BuildVersion(&'data macho::BuildVersionCommand<E>), + /// `LC_FILESET_ENTRY` + FilesetEntry(&'data macho::FilesetEntryCommand<E>), + /// An unrecognized or obsolete load command. + Other, +} + +impl<E: Endian> macho::SymtabCommand<E> { + /// Return the symbol table that this command references. + pub fn symbols<'data, Mach: MachHeader<Endian = E>, R: ReadRef<'data>>( + &self, + endian: E, + data: R, + ) -> Result<SymbolTable<'data, Mach, R>> { + 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() { + let mut commands = LoadCommandIterator::new(LittleEndian, &[0; 8], 10); + assert!(commands.next().is_err()); + let mut commands = LoadCommandIterator::new(LittleEndian, &[0, 0, 0, 0, 7, 0, 0, 0, 0], 10); + assert!(commands.next().is_err()); + let mut commands = LoadCommandIterator::new(LittleEndian, &[0, 0, 0, 0, 8, 0, 0, 0, 0], 10); + assert!(commands.next().is_ok()); + } +} diff --git a/third_party/rust/object/src/read/macho/mod.rs b/third_party/rust/object/src/read/macho/mod.rs new file mode 100644 index 0000000000..f07ed581b6 --- /dev/null +++ b/third_party/rust/object/src/read/macho/mod.rs @@ -0,0 +1,30 @@ +//! Support for reading Mach-O files. +//! +//! Defines traits to abstract over the difference between 32-bit and 64-bit +//! Mach-O files, and implements read functionality in terms of these traits. +//! +//! Also provides `MachOFile` and related types which implement the `Object` trait. + +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::*; diff --git a/third_party/rust/object/src/read/macho/relocation.rs b/third_party/rust/object/src/read/macho/relocation.rs new file mode 100644 index 0000000000..18e22ef706 --- /dev/null +++ b/third_party/rust/object/src/read/macho/relocation.rs @@ -0,0 +1,127 @@ +use core::{fmt, slice}; + +use crate::endian::Endianness; +use crate::macho; +use crate::read::{ + ReadRef, Relocation, RelocationEncoding, RelocationKind, RelocationTarget, SectionIndex, + SymbolIndex, +}; + +use super::{MachHeader, MachOFile}; + +/// An iterator over the relocations in a `MachOSection32`. +pub type MachORelocationIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachORelocationIterator<'data, 'file, macho::MachHeader32<Endian>, R>; +/// An iterator over the relocations in a `MachOSection64`. +pub type MachORelocationIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachORelocationIterator<'data, 'file, macho::MachHeader64<Endian>, R>; + +/// An iterator over the relocations in a `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<Mach::Endian>>, +} + +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<Self::Item> { + 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 mut encoding = RelocationEncoding::Generic; + let kind = match cputype { + macho::CPU_TYPE_ARM => match (reloc.r_type, reloc.r_pcrel) { + (macho::ARM_RELOC_VANILLA, false) => RelocationKind::Absolute, + _ => RelocationKind::MachO { + value: reloc.r_type, + relative: reloc.r_pcrel, + }, + }, + macho::CPU_TYPE_ARM64 | macho::CPU_TYPE_ARM64_32 => { + match (reloc.r_type, reloc.r_pcrel) { + (macho::ARM64_RELOC_UNSIGNED, false) => RelocationKind::Absolute, + _ => RelocationKind::MachO { + value: reloc.r_type, + relative: reloc.r_pcrel, + }, + } + } + macho::CPU_TYPE_X86 => match (reloc.r_type, reloc.r_pcrel) { + (macho::GENERIC_RELOC_VANILLA, false) => RelocationKind::Absolute, + _ => RelocationKind::MachO { + value: reloc.r_type, + relative: reloc.r_pcrel, + }, + }, + macho::CPU_TYPE_X86_64 => match (reloc.r_type, reloc.r_pcrel) { + (macho::X86_64_RELOC_UNSIGNED, false) => RelocationKind::Absolute, + (macho::X86_64_RELOC_SIGNED, true) => { + encoding = RelocationEncoding::X86RipRelative; + RelocationKind::Relative + } + (macho::X86_64_RELOC_BRANCH, true) => { + encoding = RelocationEncoding::X86Branch; + RelocationKind::Relative + } + (macho::X86_64_RELOC_GOT, true) => RelocationKind::GotRelative, + (macho::X86_64_RELOC_GOT_LOAD, true) => { + encoding = RelocationEncoding::X86RipRelativeMovq; + RelocationKind::GotRelative + } + _ => RelocationKind::MachO { + value: reloc.r_type, + relative: reloc.r_pcrel, + }, + }, + _ => RelocationKind::MachO { + value: reloc.r_type, + relative: reloc.r_pcrel, + }, + }; + 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 addend = if reloc.r_pcrel { -4 } else { 0 }; + return Some(( + reloc.r_address as u64, + Relocation { + kind, + encoding, + size, + target, + addend, + implicit_addend: true, + }, + )); + } + } +} + +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() + } +} diff --git a/third_party/rust/object/src/read/macho/section.rs b/third_party/rust/object/src/read/macho/section.rs new file mode 100644 index 0000000000..f43a5b83d4 --- /dev/null +++ b/third_party/rust/object/src/read/macho/section.rs @@ -0,0 +1,387 @@ +use 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, CompressedData, CompressedFileRange, ObjectSection, ReadError, ReadRef, Result, + SectionFlags, SectionIndex, SectionKind, +}; + +use super::{MachHeader, MachOFile, MachORelocationIterator}; + +/// An iterator over the sections of a `MachOFile32`. +pub type MachOSectionIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSectionIterator<'data, 'file, macho::MachHeader32<Endian>, R>; +/// An iterator over the sections of a `MachOFile64`. +pub type MachOSectionIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSectionIterator<'data, 'file, macho::MachHeader64<Endian>, R>; + +/// An iterator over the sections of 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>>, +} + +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::Item> { + self.iter.next().map(|&internal| MachOSection { + file: self.file, + internal, + }) + } +} + +/// A section of a `MachOFile32`. +pub type MachOSection32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSection<'data, 'file, macho::MachHeader32<Endian>, R>; +/// A section of a `MachOFile64`. +pub type MachOSection64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSection<'data, 'file, macho::MachHeader64<Endian>, R>; + +/// A section of a `MachOFile`. +#[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>, +} + +impl<'data, 'file, Mach, R> MachOSection<'data, 'file, Mach, R> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + fn bytes(&self) -> Result<&'data [u8]> { + let segment_index = self.internal.segment_index; + let segment = self.file.segment_internal(segment_index)?; + self.internal + .section + .data(self.file.endian, segment.data) + .read_error("Invalid Mach-O section size or offset") + } +} + +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<Option<&'data [u8]>> { + Ok(read::util::data_range( + self.bytes()?, + self.address(), + address, + size, + )) + } + + #[inline] + fn compressed_file_range(&self) -> Result<CompressedFileRange> { + Ok(CompressedFileRange::none(self.file_range())) + } + + #[inline] + fn compressed_data(&self) -> Result<CompressedData<'data>> { + self.data().map(CompressedData::none) + } + + #[inline] + fn name_bytes(&self) -> Result<&[u8]> { + Ok(self.internal.section.name()) + } + + #[inline] + fn name(&self) -> Result<&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<Option<&[u8]>> { + Ok(Some(self.internal.section.segment_name())) + } + + #[inline] + fn segment_name(&self) -> Result<Option<&str>> { + 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 + .internal + .section + .relocations(self.file.endian, self.file.data) + .unwrap_or(&[]) + .iter(), + } + } + + 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> { + pub index: SectionIndex, + pub segment_index: usize, + pub kind: SectionKind, + pub section: &'data Mach::Section, +} + +impl<'data, Mach: MachHeader> MachOSectionInternal<'data, Mach> { + pub(super) fn parse( + index: SectionIndex, + segment_index: usize, + section: &'data Mach::Section, + ) -> 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, + segment_index, + kind, + section, + } + } +} + +/// A trait for generic access to `Section32` and `Section64`. +#[allow(missing_docs)] +pub trait Section: Debug + Pod { + type Word: Into<u64>; + 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<Self::Endian>]> { + data.read_slice_at(self.reloff(endian).into(), self.nreloc(endian) as usize) + .read_error("Invalid Mach-O relocations offset or number") + } +} + +impl<Endian: endian::Endian> Section for macho::Section32<Endian> { + 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<Endian: endian::Endian> Section for macho::Section64<Endian> { + 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) + } +} diff --git a/third_party/rust/object/src/read/macho/segment.rs b/third_party/rust/object/src/read/macho/segment.rs new file mode 100644 index 0000000000..01037e1dd4 --- /dev/null +++ b/third_party/rust/object/src/read/macho/segment.rs @@ -0,0 +1,301 @@ +use 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 over the segments of a `MachOFile32`. +pub type MachOSegmentIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSegmentIterator<'data, 'file, macho::MachHeader32<Endian>, R>; +/// An iterator over the segments of a `MachOFile64`. +pub type MachOSegmentIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSegmentIterator<'data, 'file, macho::MachHeader64<Endian>, R>; + +/// An iterator over the segments of 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::Item> { + self.iter.next().map(|internal| MachOSegment { + file: self.file, + internal, + }) + } +} + +/// A segment of a `MachOFile32`. +pub type MachOSegment32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSegment<'data, 'file, macho::MachHeader32<Endian>, R>; +/// A segment of a `MachOFile64`. +pub type MachOSegment64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSegment<'data, 'file, macho::MachHeader64<Endian>, R>; + +/// A segment of a `MachOFile`. +#[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>, +{ + fn bytes(&self) -> Result<&'data [u8]> { + self.internal + .segment + .data(self.file.endian, self.file.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<Option<&'data [u8]>> { + Ok(read::util::data_range( + self.bytes()?, + self.address(), + address, + size, + )) + } + + #[inline] + fn name_bytes(&self) -> Result<Option<&[u8]>> { + Ok(Some(self.internal.segment.name())) + } + + #[inline] + fn name(&self) -> Result<Option<&str>> { + 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 data: R, + pub segment: &'data Mach::Segment, +} + +/// A trait for generic access to `SegmentCommand32` and `SegmentCommand64`. +#[allow(missing_docs)] +pub trait Segment: Debug + Pod { + type Word: Into<u64>; + type Endian: endian::Endian; + type Section: Section<Endian = Self::Endian>; + + fn from_command(command: LoadCommandData<'_, Self::Endian>) -> Result<Option<(&Self, &[u8])>>; + + 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<Endian: endian::Endian> Segment for macho::SegmentCommand32<Endian> { + type Word = u32; + type Endian = Endian; + type Section = macho::Section32<Self::Endian>; + + fn from_command(command: LoadCommandData<'_, Self::Endian>) -> Result<Option<(&Self, &[u8])>> { + 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<Endian: endian::Endian> Segment for macho::SegmentCommand64<Endian> { + type Word = u64; + type Endian = Endian; + type Section = macho::Section64<Self::Endian>; + + fn from_command(command: LoadCommandData<'_, Self::Endian>) -> Result<Option<(&Self, &[u8])>> { + 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) + } +} diff --git a/third_party/rust/object/src/read/macho/symbol.rs b/third_party/rust/object/src/read/macho/symbol.rs new file mode 100644 index 0000000000..ef88521451 --- /dev/null +++ b/third_party/rust/object/src/read/macho/symbol.rs @@ -0,0 +1,488 @@ +use 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, 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. +#[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: usize) -> Result<&'data Mach::Nlist> { + self.symbols + .get(index) + .read_error("Invalid Mach-O symbol index") + } + + /// Construct a map from addresses to a user-defined map entry. + pub fn map<Entry: SymbolMapEntry, F: Fn(&'data Mach::Nlist) -> Option<Entry>>( + &self, + f: F, + ) -> SymbolMap<Entry> { + 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()); + objects.push(name); + } + } + } + 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, + } + } +} + +/// An iterator over the symbols of a `MachOFile32`. +pub type MachOSymbolTable32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSymbolTable<'data, 'file, macho::MachHeader32<Endian>, R>; +/// An iterator over the symbols of a `MachOFile64`. +pub type MachOSymbolTable64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSymbolTable<'data, 'file, macho::MachHeader64<Endian>, R>; + +/// A symbol table of 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 { + file: self.file, + index: 0, + } + } + + fn symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol> { + let nlist = self.file.symbols.symbol(index.0)?; + MachOSymbol::new(self.file, index, nlist).read_error("Unsupported Mach-O symbol index") + } +} + +/// An iterator over the symbols of a `MachOFile32`. +pub type MachOSymbolIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSymbolIterator<'data, 'file, macho::MachHeader32<Endian>, R>; +/// An iterator over the symbols of a `MachOFile64`. +pub type MachOSymbolIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSymbolIterator<'data, 'file, macho::MachHeader64<Endian>, R>; + +/// An iterator over the symbols of a `MachOFile`. +pub struct MachOSymbolIterator<'data, 'file, Mach, R = &'data [u8]> +where + Mach: MachHeader, + R: ReadRef<'data>, +{ + pub(super) file: &'file MachOFile<'data, Mach, R>, + pub(super) index: usize, +} + +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<Self::Item> { + loop { + let index = self.index; + let nlist = self.file.symbols.symbols.get(index)?; + self.index += 1; + if let Some(symbol) = MachOSymbol::new(self.file, SymbolIndex(index), nlist) { + return Some(symbol); + } + } + } +} + +/// A symbol of a `MachOFile32`. +pub type MachOSymbol32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSymbol<'data, 'file, macho::MachHeader32<Endian>, R>; +/// A symbol of a `MachOFile64`. +pub type MachOSymbol64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + MachOSymbol<'data, 'file, macho::MachHeader64<Endian>, R>; + +/// A symbol of a `MachOFile`. +#[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<Self> { + if nlist.n_type() & macho::N_STAB != 0 { + return None; + } + Some(MachOSymbol { file, index, 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<SectionIndex, SymbolIndex> { + let n_desc = self.nlist.n_desc(self.file.endian); + SymbolFlags::MachO { n_desc } + } +} + +/// A trait for generic access to `Nlist32` and `Nlist64`. +#[allow(missing_docs)] +pub trait Nlist: Debug + Pod { + type Word: Into<u64>; + 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_UNDF + } + + /// 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<Endian: endian::Endian> Nlist for macho::Nlist32<Endian> { + 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<Endian: endian::Endian> Nlist for macho::Nlist64<Endian> { + 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) + } +} diff --git a/third_party/rust/object/src/read/mod.rs b/third_party/rust/object/src/read/mod.rs new file mode 100644 index 0000000000..8230d43ba5 --- /dev/null +++ b/third_party/rust/object/src/read/mod.rs @@ -0,0 +1,767 @@ +//! Interface for reading object files. + +use alloc::borrow::Cow; +use alloc::vec::Vec; +use core::{fmt, result}; + +use crate::common::*; + +mod read_ref; +pub use read_ref::*; + +#[cfg(feature = "std")] +mod read_cache; +#[cfg(feature = "std")] +pub use read_cache::*; + +mod util; +pub use util::*; + +#[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(&'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<T> = result::Result<T, Error>; + +trait ReadError<T> { + fn read_error(self, error: &'static str) -> Result<T>; +} + +impl<T> ReadError<T> for result::Result<T, ()> { + fn read_error(self, error: &'static str) -> Result<T> { + self.map_err(|()| Error(error)) + } +} + +impl<T> ReadError<T> for result::Result<T, Error> { + fn read_error(self, error: &'static str) -> Result<T> { + self.map_err(|_| Error(error)) + } +} + +impl<T> ReadError<T> for Option<T> { + fn read_error(self, error: &'static str) -> Result<T> { + 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::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::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::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::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. + #[cfg(feature = "archive")] + Archive, + /// A COFF object file. + #[cfg(feature = "coff")] + Coff, + /// A COFF bigobj object file. + /// + /// This supports a larger number of sections. + #[cfg(feature = "coff")] + CoffBig, + /// A Windows short import file. + #[cfg(feature = "coff")] + CoffImport, + /// A dyld cache file containing Mach-O images. + #[cfg(feature = "macho")] + DyldCache, + /// A 32-bit ELF file. + #[cfg(feature = "elf")] + Elf32, + /// A 64-bit ELF file. + #[cfg(feature = "elf")] + Elf64, + /// A 32-bit Mach-O file. + #[cfg(feature = "macho")] + MachO32, + /// A 64-bit Mach-O file. + #[cfg(feature = "macho")] + MachO64, + /// A 32-bit Mach-O fat binary. + #[cfg(feature = "macho")] + MachOFat32, + /// A 64-bit Mach-O fat binary. + #[cfg(feature = "macho")] + MachOFat64, + /// A 32-bit PE file. + #[cfg(feature = "pe")] + Pe32, + /// A 64-bit PE file. + #[cfg(feature = "pe")] + Pe64, + /// A Wasm file. + #[cfg(feature = "wasm")] + Wasm, + /// A 32-bit XCOFF file. + #[cfg(feature = "xcoff")] + Xcoff32, + /// A 64-bit XCOFF file. + #[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<FileKind> { + 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<FileKind> { + 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'] => 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', ..] => 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 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. +#[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 of a file. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct SectionIndex(pub usize); + +/// The index used to identify a symbol of a file. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct SymbolIndex(pub usize); + +/// The section where a symbol 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<SectionIndex> { + 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 symbols. +#[derive(Debug, Default, Clone)] +pub struct SymbolMap<T: SymbolMapEntry> { + symbols: Vec<T>, +} + +impl<T: SymbolMapEntry> SymbolMap<T> { + /// Construct a new symbol map. + /// + /// This function will sort the symbols by address. + pub fn new(mut symbols: Vec<T>) -> Self { + symbols.sort_unstable_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 + } +} + +/// A `SymbolMap` entry for symbol 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. +#[derive(Debug, Default, Clone)] +pub struct ObjectMap<'data> { + symbols: SymbolMap<ObjectMapEntry<'data>>, + objects: Vec<&'data [u8]>, +} + +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) -> &[&'data [u8]] { + &self.objects + } +} + +/// A `ObjectMap` entry. +#[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(&self, map: &ObjectMap<'data>) -> &'data [u8] { + map.objects[self.object] + } +} + +impl<'data> SymbolMapEntry for ObjectMapEntry<'data> { + #[inline] + fn address(&self) -> u64 { + self.address + } +} + +/// An imported symbol. +#[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. +#[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 +#[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. +#[derive(Debug)] +pub struct Relocation { + kind: RelocationKind, + encoding: RelocationEncoding, + size: u8, + target: RelocationTarget, + addend: i64, + implicit_addend: bool, +} + +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 + } +} + +/// 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. +#[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<CompressedData<'data>> { + 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. +#[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<Cow<'data, [u8]>> { + match self.format { + CompressionFormat::None => Ok(Cow::Borrowed(self.data)), + #[cfg(feature = "compression")] + CompressionFormat::Zlib => { + use core::convert::TryInto; + let size = self + .uncompressed_size + .try_into() + .ok() + .read_error("Uncompressed data size is too large.")?; + let mut decompressed = Vec::with_capacity(size); + let mut decompress = flate2::Decompress::new(true); + decompress + .decompress_vec( + self.data, + &mut decompressed, + flate2::FlushDecompress::Finish, + ) + .ok() + .read_error("Invalid zlib compressed data")?; + Ok(Cow::Owned(decompressed)) + } + #[cfg(feature = "compression")] + 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::with_capacity(size); + let mut decoder = ruzstd::StreamingDecoder::new(self.data) + .ok() + .read_error("Invalid zstd compressed data")?; + decoder + .read_to_end(&mut decompressed) + .ok() + .read_error("Invalid zstd compressed data")?; + Ok(Cow::Owned(decompressed)) + } + _ => Err(Error("Unsupported compressed data.")), + } + } +} diff --git a/third_party/rust/object/src/read/pe/data_directory.rs b/third_party/rust/object/src/read/pe/data_directory.rs new file mode 100644 index 0000000000..0e10244bf3 --- /dev/null +++ b/third_party/rust/object/src/read/pe/data_directory.rs @@ -0,0 +1,211 @@ +use core::slice; + +use crate::read::{Error, ReadError, ReadRef, Result}; +use crate::{pe, LittleEndian as LE}; + +use super::{ + DelayLoadImportTable, ExportTable, ImportTable, RelocationBlockIterator, ResourceDirectory, + SectionTable, +}; + +/// The table of data directories in a PE file. +#[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<Self> { + 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<slice::Iter<'data, pe::ImageDataDirectory>> { + 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<R: ReadRef<'data>>( + &self, + data: R, + sections: &SectionTable<'data>, + ) -> Result<Option<&'data pe::ImageExportDirectory>> { + 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<R: ReadRef<'data>>( + &self, + data: R, + sections: &SectionTable<'data>, + ) -> Result<Option<ExportTable<'data>>> { + 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<R: ReadRef<'data>>( + &self, + data: R, + sections: &SectionTable<'data>, + ) -> Result<Option<ImportTable<'data>>> { + 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<R: ReadRef<'data>>( + &self, + data: R, + sections: &SectionTable<'data>, + ) -> Result<Option<DelayLoadImportTable<'data>>> { + 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<R: ReadRef<'data>>( + &self, + data: R, + sections: &SectionTable<'data>, + ) -> Result<Option<RelocationBlockIterator<'data>>> { + 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<R: ReadRef<'data>>( + &self, + data: R, + sections: &SectionTable<'data>, + ) -> Result<Option<ResourceDirectory<'data>>> { + 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") + } +} diff --git a/third_party/rust/object/src/read/pe/export.rs b/third_party/rust/object/src/read/pe/export.rs new file mode 100644 index 0000000000..88dc78d50b --- /dev/null +++ b/third_party/rust/object/src/read/pe/export.rs @@ -0,0 +1,331 @@ +use alloc::vec::Vec; +use core::fmt::Debug; + +use crate::read::{ByteString, Bytes, Error, ReadError, ReadRef, Result}; +use crate::{pe, LittleEndian as LE, U16Bytes, U32Bytes}; + +/// 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. +#[derive(Debug, Clone)] +pub struct ExportTable<'data> { + data: Bytes<'data>, + virtual_address: u32, + directory: &'data pe::ImageExportDirectory, + addresses: &'data [U32Bytes<LE>], + names: &'data [U32Bytes<LE>], + name_ordinals: &'data [U16Bytes<LE>], +} + +impl<'data> ExportTable<'data> { + /// Parse the export table given its section data and address. + pub fn parse(data: &'data [u8], virtual_address: u32) -> Result<Self> { + 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::<U32Bytes<_>>( + 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::<U32Bytes<_>>( + address_of_names.wrapping_sub(virtual_address) as usize, + number, + ) + .read_error("Invalid PE export name pointer table")?; + name_ordinals = data + .read_slice_at::<U16Bytes<_>>( + 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::<pe::ImageExportDirectory>(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<LE>] { + 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<LE>] { + 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<LE>] { + 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<Item = (u32, u16)> + '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<u32> { + 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<u32> { + 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<ExportTarget<'data>> { + 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<ExportTarget<'data>> { + 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<ExportTarget<'data>> { + 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<usize> { + 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<Option<&'data [u8]>> { + 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<Vec<Export<'data>>> { + // 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<u32> { + 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) +} diff --git a/third_party/rust/object/src/read/pe/file.rs b/third_party/rust/object/src/read/pe/file.rs new file mode 100644 index 0000000000..0f8ce9f259 --- /dev/null +++ b/third_party/rust/object/src/read/pe/file.rs @@ -0,0 +1,1029 @@ +use alloc::vec::Vec; +use core::fmt::Debug; +use core::{mem, str}; + +use core::convert::TryInto; + +use crate::read::coff::{CoffCommon, CoffSymbol, CoffSymbolIterator, CoffSymbolTable, SymbolTable}; +use crate::read::{ + self, Architecture, ComdatKind, Error, Export, FileFlags, Import, NoDynamicRelocationIterator, + Object, ObjectComdat, ObjectKind, ReadError, ReadRef, Result, SectionIndex, SymbolIndex, +}; +use crate::{pe, ByteString, Bytes, CodeView, LittleEndian as LE, Pod, U32}; + +use super::{ + DataDirectories, ExportTable, ImageThunkData, ImportTable, PeSection, PeSectionIterator, + PeSegment, PeSegmentIterator, RichHeaderInfo, SectionTable, +}; + +/// A PE32 (32-bit) image file. +pub type PeFile32<'data, R = &'data [u8]> = PeFile<'data, pe::ImageNtHeaders32, R>; +/// A PE32+ (64-bit) image file. +pub type PeFile64<'data, R = &'data [u8]> = PeFile<'data, pe::ImageNtHeaders64, R>; + +/// A PE object file. +#[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<Self> { + 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<'_>> { + 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<Option<ExportTable<'data>>> { + 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<Option<ImportTable<'data>>> { + 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, 'file, Pe, R> Object<'data, 'file> for PeFile<'data, Pe, R> +where + 'data: 'file, + Pe: ImageNtHeaders, + R: 'file + ReadRef<'data>, +{ + type Segment = PeSegment<'data, 'file, Pe, R>; + type SegmentIterator = PeSegmentIterator<'data, 'file, Pe, R>; + type Section = PeSection<'data, 'file, Pe, R>; + type SectionIterator = PeSectionIterator<'data, 'file, Pe, R>; + type Comdat = PeComdat<'data, 'file, Pe, R>; + type ComdatIterator = PeComdatIterator<'data, 'file, Pe, R>; + type Symbol = CoffSymbol<'data, 'file, R>; + type SymbolIterator = CoffSymbolIterator<'data, 'file, R>; + type SymbolTable = CoffSymbolTable<'data, 'file, R>; + type DynamicRelocationIterator = NoDynamicRelocationIterator; + + 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 => Architecture::Aarch64, + pe::IMAGE_FILE_MACHINE_I386 => Architecture::I386, + pe::IMAGE_FILE_MACHINE_AMD64 => Architecture::X86_64, + _ => Architecture::Unknown, + } + } + + #[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(&'file self) -> PeSegmentIterator<'data, 'file, Pe, R> { + PeSegmentIterator { + file: self, + iter: self.common.sections.iter(), + } + } + + fn section_by_name_bytes( + &'file self, + section_name: &[u8], + ) -> Option<PeSection<'data, 'file, Pe, R>> { + self.common + .sections + .section_by_name(self.common.symbols.strings(), section_name) + .map(|(index, section)| PeSection { + file: self, + index: SectionIndex(index), + section, + }) + } + + fn section_by_index( + &'file self, + index: SectionIndex, + ) -> Result<PeSection<'data, 'file, Pe, R>> { + let section = self.common.sections.section(index.0)?; + Ok(PeSection { + file: self, + index, + section, + }) + } + + fn sections(&'file self) -> PeSectionIterator<'data, 'file, Pe, R> { + PeSectionIterator { + file: self, + iter: self.common.sections.iter().enumerate(), + } + } + + fn comdats(&'file self) -> PeComdatIterator<'data, 'file, Pe, R> { + PeComdatIterator { file: self } + } + + fn symbol_by_index(&'file self, index: SymbolIndex) -> Result<CoffSymbol<'data, 'file, R>> { + let symbol = self.common.symbols.symbol(index.0)?; + Ok(CoffSymbol { + file: &self.common, + index, + symbol, + }) + } + + fn symbols(&'file self) -> CoffSymbolIterator<'data, 'file, R> { + CoffSymbolIterator { + file: &self.common, + index: 0, + } + } + + fn symbol_table(&'file self) -> Option<CoffSymbolTable<'data, 'file, R>> { + Some(CoffSymbolTable { file: &self.common }) + } + + fn dynamic_symbols(&'file self) -> CoffSymbolIterator<'data, 'file, R> { + CoffSymbolIterator { + file: &self.common, + // Hack: don't return any. + index: self.common.symbols.len(), + } + } + + fn dynamic_symbol_table(&'file self) -> Option<CoffSymbolTable<'data, 'file, R>> { + None + } + + fn dynamic_relocations(&'file self) -> Option<NoDynamicRelocationIterator> { + None + } + + fn imports(&self) -> Result<Vec<Import<'data>>> { + 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::<Pe>()? { + 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<Vec<Export<'data>>> { + 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<Option<CodeView<'_>>> { + 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).map(Bytes)?; + let debug_data_size = data_dir.size.get(LE) as usize; + + let count = debug_data_size / mem::size_of::<pe::ImageDebugDirectory>(); + let rem = debug_data_size % mem::size_of::<pe::ImageDebugDirectory>(); + if rem != 0 || count < 1 { + return Err(Error("Invalid PE debug dir size")); + } + + let debug_dirs = debug_data + .read_slice_at::<pe::ImageDebugDirectory>(0, count) + .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::<u8>( + 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::<U32<LE>>().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 over the COMDAT section groups of a `PeFile32`. +pub type PeComdatIterator32<'data, 'file, R = &'data [u8]> = + PeComdatIterator<'data, 'file, pe::ImageNtHeaders32, R>; +/// An iterator over the COMDAT section groups of a `PeFile64`. +pub type PeComdatIterator64<'data, 'file, R = &'data [u8]> = + PeComdatIterator<'data, 'file, pe::ImageNtHeaders64, R>; + +/// An iterator over the COMDAT section groups of a `PeFile`. +#[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<Self::Item> { + None + } +} + +/// A COMDAT section group of a `PeFile32`. +pub type PeComdat32<'data, 'file, R = &'data [u8]> = + PeComdat<'data, 'file, pe::ImageNtHeaders32, R>; +/// A COMDAT section group of a `PeFile64`. +pub type PeComdat64<'data, 'file, R = &'data [u8]> = + PeComdat<'data, 'file, pe::ImageNtHeaders64, R>; + +/// A COMDAT section group of a `PeFile`. +#[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<&[u8]> { + unreachable!(); + } + + #[inline] + fn name(&self) -> Result<&str> { + unreachable!(); + } + + #[inline] + fn sections(&self) -> Self::SectionIterator { + unreachable!(); + } +} + +/// An iterator over the sections in a COMDAT section group of a `PeFile32`. +pub type PeComdatSectionIterator32<'data, 'file, R = &'data [u8]> = + PeComdatSectionIterator<'data, 'file, pe::ImageNtHeaders32, R>; +/// An iterator over the sections in a COMDAT section group of a `PeFile64`. +pub type PeComdatSectionIterator64<'data, 'file, R = &'data [u8]> = + PeComdatSectionIterator<'data, 'file, pe::ImageNtHeaders64, R>; + +/// An iterator over the sections in a COMDAT section group of a `PeFile`. +#[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<Self::Item> { + 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::<pe::ImageDosHeader>(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 the `optional_header.magic`. +/// +/// 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<u16> { + 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::<pe::ImageNtHeaders32>(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 `ImageNtHeaders32` and `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 `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::<Self>(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::<Self::ImageOptionalHeader>() 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<'data>> { + 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<'data, R>> { + SymbolTable::parse(self.file_header(), data) + } +} + +/// A trait for generic access to `ImageOptionalHeader32` and `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<u32>; + + // 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<u32> { + 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<u32> { + 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) + } +} diff --git a/third_party/rust/object/src/read/pe/import.rs b/third_party/rust/object/src/read/pe/import.rs new file mode 100644 index 0000000000..a5535dc367 --- /dev/null +++ b/third_party/rust/object/src/read/pe/import.rs @@ -0,0 +1,332 @@ +use core::fmt::Debug; +use core::mem; + +use crate::read::{Bytes, ReadError, Result}; +use crate::{pe, LittleEndian as LE, Pod, U16Bytes}; + +use super::ImageNtHeaders; + +/// Information for parsing a PE 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<ImportDescriptorIterator<'data>> { + 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 }) + } + + /// 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<ImportThunkList<'data>> { + 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<Pe: ImageNtHeaders>(&self, thunk: Pe::ImageThunkData) -> Result<Import<'data>> { + 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::<U16Bytes<LE>>() + .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>, +} + +impl<'data> ImportDescriptorIterator<'data> { + /// Return the next descriptor. + /// + /// Returns `Ok(None)` when a null descriptor is found. + pub fn next(&mut self) -> Result<Option<&'data pe::ImageImportDescriptor>> { + let import_desc = self + .data + .read::<pe::ImageImportDescriptor>() + .read_error("Missing PE null import descriptor")?; + if import_desc.is_null() { + Ok(None) + } else { + Ok(Some(import_desc)) + } + } +} + +/// 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<Pe: ImageNtHeaders>(&self, index: usize) -> Result<Pe::ImageThunkData> { + let thunk = self + .data + .read_at(index * mem::size_of::<Pe::ImageThunkData>()) + .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<Pe: ImageNtHeaders>(&mut self) -> Result<Option<Pe::ImageThunkData>> { + let thunk = self + .data + .read::<Pe::ImageThunkData>() + .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. +#[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<DelayLoadDescriptorIterator<'data>> { + 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 }) + } + + /// 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<ImportThunkList<'data>> { + 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<Pe: ImageNtHeaders>(&self, thunk: Pe::ImageThunkData) -> Result<Import<'data>> { + 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::<U16Bytes<LE>>() + .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>, +} + +impl<'data> DelayLoadDescriptorIterator<'data> { + /// Return the next descriptor. + /// + /// Returns `Ok(None)` when a null descriptor is found. + pub fn next(&mut self) -> Result<Option<&'data pe::ImageDelayloadDescriptor>> { + let import_desc = self + .data + .read::<pe::ImageDelayloadDescriptor>() + .read_error("Missing PE null delay-load import descriptor")?; + if import_desc.is_null() { + Ok(None) + } else { + Ok(Some(import_desc)) + } + } +} diff --git a/third_party/rust/object/src/read/pe/mod.rs b/third_party/rust/object/src/read/pe/mod.rs new file mode 100644 index 0000000000..2b7cc5d7a0 --- /dev/null +++ b/third_party/rust/object/src/read/pe/mod.rs @@ -0,0 +1,34 @@ +//! Support for reading PE files. +//! +//! Defines traits to abstract over the difference between PE32/PE32+, +//! and implements read functionality in terms of these traits. +//! +//! This module reuses some of the COFF functionality. +//! +//! Also provides `PeFile` and related types which implement the `Object` trait. + +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}; diff --git a/third_party/rust/object/src/read/pe/relocation.rs b/third_party/rust/object/src/read/pe/relocation.rs new file mode 100644 index 0000000000..06215bd1a7 --- /dev/null +++ b/third_party/rust/object/src/read/pe/relocation.rs @@ -0,0 +1,90 @@ +use 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. +#[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<Option<RelocationIterator<'data>>> { + if self.data.is_empty() { + return Ok(None); + } + let header = self + .data + .read::<pe::ImageBaseRelocation>() + .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::<U16<LE>>(count as usize) + .read_error("Invalid PE reloc block size")? + .iter(); + Ok(Some(RelocationIterator { + virtual_address, + size, + relocs, + })) + } +} + +/// 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<LE>>, +} + +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<Relocation> { + 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, +} diff --git a/third_party/rust/object/src/read/pe/resource.rs b/third_party/rust/object/src/read/pe/resource.rs new file mode 100644 index 0000000000..646eaefaaa --- /dev/null +++ b/third_party/rust/object/src/read/pe/resource.rs @@ -0,0 +1,207 @@ +use alloc::string::String; +use core::char; + +use crate::read::{ReadError, ReadRef, Result}; +use crate::{pe, LittleEndian as LE, U16Bytes}; + +/// The `.rsrc` section of a PE file. +#[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<'data>> { + 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<Self> { + let mut offset = u64::from(offset); + let header = data + .read::<pe::ImageResourceDirectory>(&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::<pe::ImageResourceDirectoryEntry>(&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<ResourceDirectoryEntryData<'data>> { + if self.is_table() { + ResourceDirectoryTable::parse(section.data, self.data_offset()) + .map(ResourceDirectoryEntryData::Table) + } else { + section + .data + .read_at::<pe::ImageResourceDataEntry>(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<ResourceDirectoryTable<'data>> { + 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<String> { + 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::<String>()) + } + + /// Returns the string unicode buffer. + pub fn data<'data>( + &self, + directory: ResourceDirectory<'data>, + ) -> Result<&'data [U16Bytes<LE>]> { + let mut offset = u64::from(self.offset); + let len = directory + .data + .read::<U16Bytes<LE>>(&mut offset) + .read_error("Invalid resource name offset")?; + directory + .data + .read_slice::<U16Bytes<LE>>(&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<ResourceName> { + match self { + Self::Name(name) => Some(name), + _ => None, + } + } + + /// Converts to an option of ID. + /// + /// Helper for iterator filtering. + pub fn id(self) -> Option<u16> { + match self { + Self::Id(id) => Some(id), + _ => None, + } + } +} diff --git a/third_party/rust/object/src/read/pe/rich.rs b/third_party/rust/object/src/read/pe/rich.rs new file mode 100644 index 0000000000..33dd039c99 --- /dev/null +++ b/third_party/rust/object/src/read/pe/rich.rs @@ -0,0 +1,91 @@ +//! PE rich header handling + +use core::mem; + +use crate::pod::bytes_of_slice; +use crate::read::Bytes; +use crate::{pe, LittleEndian as LE, ReadRef, U32}; + +/// 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<R: ReadRef<'data>>(data: R, nt_header_offset: u64) -> Option<Self> { + // 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::<U32<LE>>(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::<pe::MaskedRichHeaderEntry>(); + 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<Item = RichHeaderEntry> + '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<usize> { + let mut offset = 0; + loop { + if data.get(offset..)?.get(..needle.len())? == needle { + return Some(offset); + } + offset += align; + } +} diff --git a/third_party/rust/object/src/read/pe/section.rs b/third_party/rust/object/src/read/pe/section.rs new file mode 100644 index 0000000000..2880e401fe --- /dev/null +++ b/third_party/rust/object/src/read/pe/section.rs @@ -0,0 +1,434 @@ +use 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, Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags, +}; + +use super::{ImageNtHeaders, PeFile, SectionTable}; + +/// An iterator over the loadable sections of a `PeFile32`. +pub type PeSegmentIterator32<'data, 'file, R = &'data [u8]> = + PeSegmentIterator<'data, 'file, pe::ImageNtHeaders32, R>; +/// An iterator over the loadable sections of a `PeFile64`. +pub type PeSegmentIterator64<'data, 'file, R = &'data [u8]> = + PeSegmentIterator<'data, 'file, pe::ImageNtHeaders64, R>; + +/// An iterator over the loadable sections of 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::Item> { + self.iter.next().map(|section| PeSegment { + file: self.file, + section, + }) + } +} + +/// A loadable section of a `PeFile32`. +pub type PeSegment32<'data, 'file, R = &'data [u8]> = + PeSegment<'data, 'file, pe::ImageNtHeaders32, R>; +/// A loadable section of a `PeFile64`. +pub type PeSegment64<'data, 'file, R = &'data [u8]> = + PeSegment<'data, 'file, pe::ImageNtHeaders64, R>; + +/// A loadable section of a `PeFile`. +#[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> 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<Option<&'data [u8]>> { + Ok(read::util::data_range( + self.data()?, + self.address(), + address, + size, + )) + } + + #[inline] + fn name_bytes(&self) -> Result<Option<&[u8]>> { + self.section + .name(self.file.common.symbols.strings()) + .map(Some) + } + + #[inline] + fn name(&self) -> Result<Option<&str>> { + 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 over the sections of a `PeFile32`. +pub type PeSectionIterator32<'data, 'file, R = &'data [u8]> = + PeSectionIterator<'data, 'file, pe::ImageNtHeaders32, R>; +/// An iterator over the sections of a `PeFile64`. +pub type PeSectionIterator64<'data, 'file, R = &'data [u8]> = + PeSectionIterator<'data, 'file, pe::ImageNtHeaders64, R>; + +/// An iterator over the sections of 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<slice::Iter<'data, pe::ImageSectionHeader>>, +} + +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::Item> { + self.iter.next().map(|(index, section)| PeSection { + file: self.file, + index: SectionIndex(index + 1), + section, + }) + } +} + +/// A section of a `PeFile32`. +pub type PeSection32<'data, 'file, R = &'data [u8]> = + PeSection<'data, 'file, pe::ImageNtHeaders32, R>; +/// A section of a `PeFile64`. +pub type PeSection64<'data, 'file, R = &'data [u8]> = + PeSection<'data, 'file, pe::ImageNtHeaders64, R>; + +/// A section of a `PeFile`. +#[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> 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<Option<&'data [u8]>> { + Ok(read::util::data_range( + self.data()?, + self.address(), + address, + size, + )) + } + + #[inline] + fn compressed_file_range(&self) -> Result<CompressedFileRange> { + Ok(CompressedFileRange::none(self.file_range())) + } + + #[inline] + fn compressed_data(&self) -> Result<CompressedData<'data>> { + self.data().map(CompressedData::none) + } + + #[inline] + fn name_bytes(&self) -> Result<&[u8]> { + self.section.name(self.file.common.symbols.strings()) + } + + #[inline] + fn name(&self) -> Result<&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<Option<&[u8]>> { + Ok(None) + } + + #[inline] + fn segment_name(&self) -> Result<Option<&str>> { + Ok(None) + } + + #[inline] + fn kind(&self) -> SectionKind { + self.section.kind() + } + + fn relocations(&self) -> PeRelocationIterator<'data, 'file, R> { + PeRelocationIterator(PhantomData) + } + + 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<R: ReadRef<'data>>(&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<R: ReadRef<'data>>( + &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 over the relocations in an `PeSection`. +#[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<Self::Item> { + None + } +} diff --git a/third_party/rust/object/src/read/read_cache.rs b/third_party/rust/object/src/read/read_cache.rs new file mode 100644 index 0000000000..dfce1e1b1e --- /dev/null +++ b/third_party/rust/object/src/read/read_cache.rs @@ -0,0 +1,182 @@ +use core::ops::Range; +use std::boxed::Box; +use std::cell::RefCell; +use std::collections::hash_map::Entry; +use std::collections::HashMap; +use std::convert::TryInto; +use std::io::{Read, Seek, SeekFrom}; +use std::mem; +use std::vec::Vec; + +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. +#[derive(Debug)] +pub struct ReadCache<R: Read + Seek> { + cache: RefCell<ReadCacheInternal<R>>, +} + +#[derive(Debug)] +struct ReadCacheInternal<R: Read + Seek> { + read: R, + bufs: HashMap<(u64, u64), Box<[u8]>>, + strings: HashMap<(u64, u8), Box<[u8]>>, +} + +impl<R: Read + Seek> ReadCache<R> { + /// Create an empty `ReadCache` for the given stream. + pub fn new(read: R) -> Self { + ReadCache { + cache: RefCell::new(ReadCacheInternal { + read, + bufs: HashMap::new(), + strings: HashMap::new(), + }), + } + } + + /// 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<R>`, returning the underlying reader. + pub fn into_inner(self) -> R { + self.cache.into_inner().read + } +} + +impl<'a, R: Read + Seek> ReadRef<'a> for &'a ReadCache<R> { + fn len(self) -> Result<u64, ()> { + let cache = &mut *self.cache.borrow_mut(); + cache.read.seek(SeekFrom::End(0)).map_err(|_| ()) + } + + fn read_bytes_at(self, offset: u64, size: u64) -> Result<&'a [u8], ()> { + if size == 0 { + return Ok(&[]); + } + let cache = &mut *self.cache.borrow_mut(); + 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(SeekFrom::Start(offset)).map_err(|_| ())?; + let mut bytes = vec![0; size].into_boxed_slice(); + cache.read.read_exact(&mut bytes).map_err(|_| ())?; + 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<u64>, delimiter: u8) -> Result<&'a [u8], ()> { + let cache = &mut *self.cache.borrow_mut(); + let buf = match cache.strings.entry((range.start, delimiter)) { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => { + cache + .read + .seek(SeekFrom::Start(range.start)) + .map_err(|_| ())?; + + 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..]).map_err(|_| ())?; + 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: Read + Seek> { + r: &'a ReadCache<R>, + offset: u64, + size: u64, +} + +impl<'a, R: Read + Seek> Clone for ReadCacheRange<'a, R> { + fn clone(&self) -> Self { + Self { + r: self.r, + offset: self.offset, + size: self.size, + } + } +} + +impl<'a, R: Read + Seek> Copy for ReadCacheRange<'a, R> {} + +impl<'a, R: Read + Seek> ReadRef<'a> for ReadCacheRange<'a, R> { + fn len(self) -> Result<u64, ()> { + 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<u64>, 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) + } +} diff --git a/third_party/rust/object/src/read/read_ref.rs b/third_party/rust/object/src/read/read_ref.rs new file mode 100644 index 0000000000..a9b4252214 --- /dev/null +++ b/third_party/rust/object/src/read/read_ref.rs @@ -0,0 +1,137 @@ +#![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<T> = result::Result<T, ()>; + +/// 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. +/// +/// 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<u64>; + + /// 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<u64>, 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<T: Pod>(self, offset: &mut u64) -> Result<&'a T> { + let size = mem::size_of::<T>().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<T: Pod>(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<T: Pod>(self, offset: &mut u64, count: usize) -> Result<&'a [T]> { + let size = count + .checked_mul(mem::size_of::<T>()) + .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<T: Pod>(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<u64> { + 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<u64>, 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(()), + } + } +} diff --git a/third_party/rust/object/src/read/traits.rs b/third_party/rust/object/src/read/traits.rs new file mode 100644 index 0000000000..d35b0b0caa --- /dev/null +++ b/third_party/rust/object/src/read/traits.rs @@ -0,0 +1,469 @@ +use alloc::borrow::Cow; +use alloc::vec::Vec; + +use crate::read::{ + self, Architecture, CodeView, ComdatKind, CompressedData, CompressedFileRange, Export, + FileFlags, Import, ObjectKind, ObjectMap, Relocation, Result, SectionFlags, SectionIndex, + SectionKind, SegmentFlags, SymbolFlags, SymbolIndex, SymbolKind, SymbolMap, SymbolMapName, + SymbolScope, SymbolSection, +}; +use crate::Endianness; + +/// An object file. +pub trait Object<'data: 'file, 'file>: read::private::Sealed { + /// A segment in the object file. + type Segment: ObjectSegment<'data>; + + /// An iterator over the segments in the object file. + type SegmentIterator: Iterator<Item = Self::Segment>; + + /// A section in the object file. + type Section: ObjectSection<'data>; + + /// An iterator over the sections in the object file. + type SectionIterator: Iterator<Item = Self::Section>; + + /// A COMDAT section group in the object file. + type Comdat: ObjectComdat<'data>; + + /// An iterator over the COMDAT section groups in the object file. + type ComdatIterator: Iterator<Item = Self::Comdat>; + + /// A symbol in the object file. + type Symbol: ObjectSymbol<'data>; + + /// An iterator over symbols in the object file. + type SymbolIterator: Iterator<Item = Self::Symbol>; + + /// A symbol table in the object file. + type SymbolTable: ObjectSymbolTable< + 'data, + Symbol = Self::Symbol, + SymbolIterator = Self::SymbolIterator, + >; + + /// An iterator over dynamic relocations in the file. + /// + /// The first field in the item tuple is the address + /// that the relocation applies to. + type DynamicRelocationIterator: Iterator<Item = (u64, Relocation)>; + + /// Get the architecture type of the file. + fn architecture(&self) -> Architecture; + + /// 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 over the segments in the file. + fn segments(&'file 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). + /// + /// 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(&'file self, section_name: &str) -> Option<Self::Section> { + 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 self, section_name: &[u8]) -> Option<Self::Section>; + + /// 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(&'file self, index: SectionIndex) -> Result<Self::Section>; + + /// Get an iterator over the sections in the file. + fn sections(&'file self) -> Self::SectionIterator; + + /// Get an iterator over the COMDAT section groups in the file. + fn comdats(&'file self) -> Self::ComdatIterator; + + /// Get the symbol table, if any. + fn symbol_table(&'file self) -> Option<Self::SymbolTable>; + + /// 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(&'file self, index: SymbolIndex) -> Result<Self::Symbol>; + + /// Get an iterator over 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(&'file self) -> Self::SymbolIterator; + + /// Get the dynamic linking symbol table, if any. + /// + /// Only ELF has a separate dynamic linking symbol table. + fn dynamic_symbol_table(&'file self) -> Option<Self::SymbolTable>; + + /// Get an iterator over the dynamic linking symbols in the file. + /// + /// This may skip over symbols that are malformed or unsupported. + /// + /// Only ELF has separate dynamic linking symbols. + /// Other file formats will return an empty iterator. + fn dynamic_symbols(&'file 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(&'file self) -> Option<Self::DynamicRelocationIterator>; + + /// 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(&'file self) -> SymbolMap<SymbolMapName<'data>> { + let mut symbols = Vec::new(); + if let Some(table) = self.symbol_table().or_else(|| self.dynamic_symbol_table()) { + for symbol in table.symbols() { + if !symbol.is_definition() { + continue; + } + if let Ok(name) = symbol.name() { + symbols.push(SymbolMapName::new(symbol.address(), name)); + } + } + } + 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(&'file self) -> ObjectMap<'data> { + ObjectMap::default() + } + + /// Get the imported symbols. + fn imports(&self) -> Result<Vec<Import<'data>>>; + + /// 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 lower-level API. + fn exports(&self) -> Result<Vec<Export<'data>>>; + + /// Return true if the file contains debug information sections, false if not. + fn has_debug_symbols(&self) -> bool; + + /// The UUID from a Mach-O `LC_UUID` load command. + #[inline] + fn mach_uuid(&self) -> Result<Option<[u8; 16]>> { + Ok(None) + } + + /// The build ID from an ELF `NT_GNU_BUILD_ID` note. + #[inline] + fn build_id(&self) -> Result<Option<&'data [u8]>> { + Ok(None) + } + + /// The filename and CRC from a `.gnu_debuglink` section. + #[inline] + fn gnu_debuglink(&self) -> Result<Option<(&'data [u8], u32)>> { + Ok(None) + } + + /// The filename and build ID from a `.gnu_debugaltlink` section. + #[inline] + fn gnu_debugaltlink(&self) -> Result<Option<(&'data [u8], &'data [u8])>> { + Ok(None) + } + + /// The filename and GUID from the PE CodeView section + #[inline] + fn pdb_info(&self) -> Result<Option<CodeView<'_>>> { + Ok(None) + } + + /// Get the base address used for relative virtual addresses. + /// + /// Currently this is only non-zero for PE. + fn relative_address_base(&'file self) -> u64; + + /// Get the virtual address of the entry point of the binary + fn entry(&'file self) -> u64; + + /// File flags that are specific to each file format. + fn flags(&self) -> FileFlags; +} + +/// A loadable segment defined in an object file. +/// +/// For ELF, this is a program header with type `PT_LOAD`. +/// For Mach-O, this is a load command with type `LC_SEGMENT` or `LC_SEGMENT_64`. +pub trait ObjectSegment<'data>: 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<Option<&'data [u8]>>; + + /// Returns the name of the segment. + fn name_bytes(&self) -> Result<Option<&[u8]>>; + + /// Returns the name of the segment. + /// + /// Returns an error if the name is not UTF-8. + fn name(&self) -> Result<Option<&str>>; + + /// Return the flags of segment. + fn flags(&self) -> SegmentFlags; +} + +/// A section defined in an object file. +pub trait ObjectSection<'data>: read::private::Sealed { + /// An iterator over the relocations for a section. + /// + /// The first field in the item tuple is the section offset + /// that the relocation applies to. + type RelocationIterator: Iterator<Item = (u64, Relocation)>; + + /// 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<Option<&'data [u8]>>; + + /// Returns the potentially compressed file range of the section, + /// along with information about the compression. + fn compressed_file_range(&self) -> Result<CompressedFileRange>; + + /// Returns the potentially compressed contents of the section, + /// along with information about the compression. + fn compressed_data(&self) -> Result<CompressedData<'data>>; + + /// 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<Cow<'data, [u8]>> { + self.compressed_data()?.decompress() + } + + /// Returns the name of the section. + fn name_bytes(&self) -> Result<&[u8]>; + + /// Returns the name of the section. + /// + /// Returns an error if the name is not UTF-8. + fn name(&self) -> Result<&str>; + + /// Returns the name of the segment for this section. + fn segment_name_bytes(&self) -> Result<Option<&[u8]>>; + + /// Returns the name of the segment for this section. + /// + /// Returns an error if the name is not UTF-8. + fn segment_name(&self) -> Result<Option<&str>>; + + /// Return the kind of this section. + fn kind(&self) -> SectionKind; + + /// Get the relocations for this section. + fn relocations(&self) -> Self::RelocationIterator; + + /// Section flags that are specific to each file format. + fn flags(&self) -> SectionFlags; +} + +/// A COMDAT section group defined in an object file. +pub trait ObjectComdat<'data>: read::private::Sealed { + /// An iterator over the sections in the object file. + type SectionIterator: Iterator<Item = SectionIndex>; + + /// 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<&[u8]>; + + /// Returns the name of the COMDAT section group. + /// + /// Returns an error if the name is not UTF-8. + fn name(&self) -> Result<&str>; + + /// Get the sections in this section group. + fn sections(&self) -> Self::SectionIterator; +} + +/// A symbol table. +pub trait ObjectSymbolTable<'data>: read::private::Sealed { + /// A symbol table entry. + type Symbol: ObjectSymbol<'data>; + + /// An iterator over the symbols in a symbol table. + type SymbolIterator: Iterator<Item = Self::Symbol>; + + /// Get an iterator over 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<Self::Symbol>; +} + +/// A symbol table entry. +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<SectionIndex> { + 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. + 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<SectionIndex, SymbolIndex>; +} + +/// 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<Self::Item> { + None + } +} diff --git a/third_party/rust/object/src/read/util.rs b/third_party/rust/object/src/read/util.rs new file mode 100644 index 0000000000..7c3c65ec9e --- /dev/null +++ b/third_party/rust/object/src/read/util.rs @@ -0,0 +1,425 @@ +use 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::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<Bytes<'data>, ()> { + 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<Bytes<'data>, ()> { + 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<T: Pod>(&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<T: Pod>(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<T: Pod>(&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<T: Pod>(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<u64, ()> { + let mut result = 0; + let mut shift = 0; + + loop { + let byte = *self.read::<u8>()?; + 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<i64, ()> { + let mut result = 0; + let mut shift = 0; + + loop { + let byte = *self.read::<u8>()?; + 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 for most file formats. +#[derive(Debug, Clone, Copy)] +pub struct StringTable<'data, R = &'data [u8]> +where + R: ReadRef<'data>, +{ + data: Option<R>, + 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::<u16>(), Ok(&u16::to_be(0x0123))); + assert_eq!(bytes, Bytes(&[0x45, 0x67])); + assert_eq!(data.read_at::<u16>(2), Ok(&u16::to_be(0x4567))); + assert_eq!(data.read_at::<u16>(3), Err(())); + assert_eq!(data.read_at::<u16>(4), Err(())); + + let mut bytes = data; + assert_eq!(bytes.read::<u32>(), Ok(&x)); + assert_eq!(bytes, Bytes(&[])); + + let mut bytes = data; + assert_eq!(bytes.read::<u64>(), Err(())); + assert_eq!(bytes, Bytes(&[])); + + let mut bytes = data; + assert_eq!(bytes.read_slice::<u8>(0), Ok(&[][..])); + assert_eq!(bytes, data); + + let mut bytes = data; + assert_eq!(bytes.read_slice::<u8>(4), Ok(data.0)); + assert_eq!(bytes, Bytes(&[])); + + let mut bytes = data; + assert_eq!(bytes.read_slice::<u8>(5), Err(())); + assert_eq!(bytes, Bytes(&[])); + + assert_eq!(data.read_slice_at::<u8>(0, 0), Ok(&[][..])); + assert_eq!(data.read_slice_at::<u8>(4, 0), Ok(&[][..])); + assert_eq!(data.read_slice_at::<u8>(0, 4), Ok(data.0)); + assert_eq!(data.read_slice_at::<u8>(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]" + ); + } +} diff --git a/third_party/rust/object/src/read/wasm.rs b/third_party/rust/object/src/read/wasm.rs new file mode 100644 index 0000000000..b950ef2b2a --- /dev/null +++ b/third_party/rust/object/src/read/wasm.rs @@ -0,0 +1,951 @@ +//! Support for reading Wasm files. +//! +//! Provides `WasmFile` and related types which implement the `Object` trait. +//! +//! Currently implements the minimum required to access DWARF debugging information. +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, 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, +} +// Update this constant when adding new section id: +const MAX_SECTION_ID: usize = SectionId::DataCount 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<SectionHeader<'data>>, + // Indices into `sections` of sections with a non-zero id. + id_sections: Box<[Option<usize>; 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<WasmSymbolInternal<'data>>, + // Address of the function body for the entry point. + entry: u64, + marker: PhantomData<R>, +} + +#[derive(Debug)] +struct SectionHeader<'data> { + id: SectionId, + range: Range<usize>, + name: &'data str, +} + +#[derive(Clone)] +enum LocalFunctionKind { + Unknown, + Exported { symbol_ids: Vec<u32> }, + Local { symbol_id: u32 }, +} + +impl<T> ReadError<T> for wasmparser::Result<T> { + fn read_error(self, error: &'static str) -> Result<T> { + self.map_err(|_| Error(error)) + } +} + +impl<'data, R: ReadRef<'data>> WasmFile<'data, R> { + /// Parse the raw wasm data. + pub fn parse(data: R) -> Result<Self> { + 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::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 = + &mut local_func_kinds[local_func_id as usize]; + 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 = &mut local_func_kinds[i]; + 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::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" { + for name in + wp::NameSectionReader::new(section.data(), section.data_offset()) + { + // 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<usize>, 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, 'file, R: ReadRef<'data>> Object<'data, 'file> for WasmFile<'data, R> +where + 'data: 'file, + R: 'file, +{ + type Segment = WasmSegment<'data, 'file, R>; + type SegmentIterator = WasmSegmentIterator<'data, 'file, R>; + type Section = WasmSection<'data, 'file, R>; + type SectionIterator = WasmSectionIterator<'data, 'file, R>; + type Comdat = WasmComdat<'data, 'file, R>; + type ComdatIterator = WasmComdatIterator<'data, 'file, R>; + type Symbol = WasmSymbol<'data, 'file>; + type SymbolIterator = WasmSymbolIterator<'data, 'file>; + type SymbolTable = WasmSymbolTable<'data, 'file>; + type DynamicRelocationIterator = NoDynamicRelocationIterator; + + #[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(&'file self) -> Self::SegmentIterator { + WasmSegmentIterator { file: self } + } + + fn section_by_name_bytes( + &'file self, + section_name: &[u8], + ) -> Option<WasmSection<'data, 'file, R>> { + self.sections() + .find(|section| section.name_bytes() == Ok(section_name)) + } + + fn section_by_index(&'file self, index: SectionIndex) -> Result<WasmSection<'data, 'file, R>> { + // 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(&'file self) -> Self::SectionIterator { + WasmSectionIterator { + file: self, + sections: self.sections.iter(), + } + } + + fn comdats(&'file self) -> Self::ComdatIterator { + WasmComdatIterator { file: self } + } + + #[inline] + fn symbol_by_index(&'file self, index: SymbolIndex) -> Result<WasmSymbol<'data, 'file>> { + let symbol = self + .symbols + .get(index.0) + .read_error("Invalid Wasm symbol index")?; + Ok(WasmSymbol { index, symbol }) + } + + fn symbols(&'file self) -> Self::SymbolIterator { + WasmSymbolIterator { + symbols: self.symbols.iter().enumerate(), + } + } + + fn symbol_table(&'file self) -> Option<WasmSymbolTable<'data, 'file>> { + Some(WasmSymbolTable { + symbols: &self.symbols, + }) + } + + fn dynamic_symbols(&'file self) -> Self::SymbolIterator { + WasmSymbolIterator { + symbols: [].iter().enumerate(), + } + } + + #[inline] + fn dynamic_symbol_table(&'file self) -> Option<WasmSymbolTable<'data, 'file>> { + None + } + + #[inline] + fn dynamic_relocations(&self) -> Option<NoDynamicRelocationIterator> { + None + } + + fn imports(&self) -> Result<Vec<Import<'data>>> { + // TODO: return entries in the import section + Ok(Vec::new()) + } + + fn exports(&self) -> Result<Vec<Export<'data>>> { + // 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(&'file self) -> u64 { + self.entry + } + + #[inline] + fn flags(&self) -> FileFlags { + FileFlags::None + } +} + +/// An iterator over the segments of a `WasmFile`. +#[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<Self::Item> { + None + } +} + +/// A segment of a `WasmFile`. +#[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<Option<&'data [u8]>> { + unreachable!() + } + + #[inline] + fn name_bytes(&self) -> Result<Option<&[u8]>> { + unreachable!() + } + + #[inline] + fn name(&self) -> Result<Option<&str>> { + unreachable!() + } + + #[inline] + fn flags(&self) -> SegmentFlags { + unreachable!() + } +} + +/// An iterator over the sections of 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<Self::Item> { + let section = self.sections.next()?; + Some(WasmSection { + file: self.file, + section, + }) + } +} + +/// A section of a `WasmFile`. +#[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<Option<&'data [u8]>> { + unimplemented!() + } + + #[inline] + fn compressed_file_range(&self) -> Result<CompressedFileRange> { + Ok(CompressedFileRange::none(self.file_range())) + } + + #[inline] + fn compressed_data(&self) -> Result<CompressedData<'data>> { + self.data().map(CompressedData::none) + } + + #[inline] + fn name_bytes(&self) -> Result<&[u8]> { + self.name().map(str::as_bytes) + } + + #[inline] + fn name(&self) -> Result<&str> { + Ok(match self.section.id { + SectionId::Custom => self.section.name, + SectionId::Type => "<type>", + SectionId::Import => "<import>", + SectionId::Function => "<function>", + SectionId::Table => "<table>", + SectionId::Memory => "<memory>", + SectionId::Global => "<global>", + SectionId::Export => "<export>", + SectionId::Start => "<start>", + SectionId::Element => "<element>", + SectionId::Code => "<code>", + SectionId::Data => "<data>", + SectionId::DataCount => "<data_count>", + }) + } + + #[inline] + fn segment_name_bytes(&self) -> Result<Option<&[u8]>> { + Ok(None) + } + + #[inline] + fn segment_name(&self) -> Result<Option<&str>> { + 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, + } + } + + #[inline] + fn relocations(&self) -> WasmRelocationIterator<'data, 'file, R> { + WasmRelocationIterator(PhantomData) + } + + #[inline] + fn flags(&self) -> SectionFlags { + SectionFlags::None + } +} + +/// An iterator over the COMDAT section groups of a `WasmFile`. +#[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<Self::Item> { + None + } +} + +/// A COMDAT section group of a `WasmFile`. +#[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<&[u8]> { + unreachable!(); + } + + #[inline] + fn name(&self) -> Result<&str> { + unreachable!(); + } + + #[inline] + fn sections(&self) -> Self::SectionIterator { + unreachable!(); + } +} + +/// An iterator over the sections in a COMDAT section group of a `WasmFile`. +#[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<Self::Item> { + None + } +} + +/// A symbol table of 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<Self::Symbol> { + let symbol = self + .symbols + .get(index.0) + .read_error("Invalid Wasm symbol index")?; + Ok(WasmSymbol { index, symbol }) + } +} + +/// An iterator over the symbols of a `WasmFile`. +#[derive(Debug)] +pub struct WasmSymbolIterator<'data, 'file> { + symbols: core::iter::Enumerate<slice::Iter<'file, WasmSymbolInternal<'data>>>, +} + +impl<'data, 'file> Iterator for WasmSymbolIterator<'data, 'file> { + type Item = WasmSymbol<'data, 'file>; + + fn next(&mut self) -> Option<Self::Item> { + let (index, symbol) = self.symbols.next()?; + Some(WasmSymbol { + index: SymbolIndex(index), + symbol, + }) + } +} + +/// A symbol of a `WasmFile`. +#[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.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<SectionIndex, SymbolIndex> { + SymbolFlags::None + } +} + +/// An iterator over the relocations in a `WasmSection`. +#[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<Self::Item> { + None + } +} diff --git a/third_party/rust/object/src/read/xcoff/comdat.rs b/third_party/rust/object/src/read/xcoff/comdat.rs new file mode 100644 index 0000000000..2b23d1dba8 --- /dev/null +++ b/third_party/rust/object/src/read/xcoff/comdat.rs @@ -0,0 +1,129 @@ +//! XCOFF doesn't support the COMDAT section. + +use core::fmt::Debug; + +use crate::xcoff; + +use crate::read::{self, ComdatKind, ObjectComdat, ReadRef, Result, SectionIndex, SymbolIndex}; + +use super::{FileHeader, XcoffFile}; + +/// An iterator over the COMDAT section groups of a `XcoffFile32`. +pub type XcoffComdatIterator32<'data, 'file, R = &'data [u8]> = + XcoffComdatIterator<'data, 'file, xcoff::FileHeader32, R>; +/// An iterator over the COMDAT section groups of a `XcoffFile64`. +pub type XcoffComdatIterator64<'data, 'file, R = &'data [u8]> = + XcoffComdatIterator<'data, 'file, xcoff::FileHeader64, R>; + +/// An iterator over the COMDAT section groups of a `XcoffFile`. +#[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<Self::Item> { + None + } +} + +/// A COMDAT section group of a `XcoffFile32`. +pub type XcoffComdat32<'data, 'file, R = &'data [u8]> = + XcoffComdat<'data, 'file, xcoff::FileHeader32, R>; + +/// A COMDAT section group of a `XcoffFile64`. +pub type XcoffComdat64<'data, 'file, R = &'data [u8]> = + XcoffComdat<'data, 'file, xcoff::FileHeader64, R>; + +/// A COMDAT section group of a `XcoffFile`. +#[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<&[u8]> { + unreachable!(); + } + + #[inline] + fn name(&self) -> Result<&str> { + unreachable!(); + } + + #[inline] + fn sections(&self) -> Self::SectionIterator { + unreachable!(); + } +} + +/// An iterator over the sections in a COMDAT section group of a `XcoffFile32`. +pub type XcoffComdatSectionIterator32<'data, 'file, R = &'data [u8]> = + XcoffComdatSectionIterator<'data, 'file, xcoff::FileHeader32, R>; +/// An iterator over the sections in a COMDAT section group of a `XcoffFile64`. +pub type XcoffComdatSectionIterator64<'data, 'file, R = &'data [u8]> = + XcoffComdatSectionIterator<'data, 'file, xcoff::FileHeader64, R>; + +/// An iterator over the sections in a COMDAT section group of a `XcoffFile`. +#[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<Self::Item> { + None + } +} diff --git a/third_party/rust/object/src/read/xcoff/file.rs b/third_party/rust/object/src/read/xcoff/file.rs new file mode 100644 index 0000000000..bac9e70756 --- /dev/null +++ b/third_party/rust/object/src/read/xcoff/file.rs @@ -0,0 +1,629 @@ +use core::fmt::Debug; +use core::mem; + +use alloc::vec::Vec; + +use crate::read::{self, Error, NoDynamicRelocationIterator, Object, ReadError, ReadRef, Result}; + +use crate::{ + xcoff, Architecture, BigEndian as BE, FileFlags, ObjectKind, ObjectSection, Pod, SectionIndex, + SymbolIndex, +}; + +use super::{ + CsectAux, FileAux, SectionHeader, SectionTable, Symbol, SymbolTable, XcoffComdat, + XcoffComdatIterator, XcoffSection, XcoffSectionIterator, XcoffSegment, XcoffSegmentIterator, + XcoffSymbol, XcoffSymbolIterator, XcoffSymbolTable, +}; + +/// A 32-bit XCOFF object file. +pub type XcoffFile32<'data, R = &'data [u8]> = XcoffFile<'data, xcoff::FileHeader32, R>; +/// A 64-bit XCOFF object file. +pub type XcoffFile64<'data, R = &'data [u8]> = XcoffFile<'data, xcoff::FileHeader64, R>; + +/// A partially parsed XCOFF file. +/// +/// Most of the functionality of this type 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<Self> { + 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. + pub fn raw_header(&self) -> &'data Xcoff { + self.header + } +} + +impl<'data, Xcoff, R> read::private::Sealed for XcoffFile<'data, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Xcoff, R> Object<'data, 'file> for XcoffFile<'data, Xcoff, R> +where + 'data: 'file, + Xcoff: FileHeader, + R: 'file + ReadRef<'data>, +{ + type Segment = XcoffSegment<'data, 'file, Xcoff, R>; + type SegmentIterator = XcoffSegmentIterator<'data, 'file, Xcoff, R>; + type Section = XcoffSection<'data, 'file, Xcoff, R>; + type SectionIterator = XcoffSectionIterator<'data, 'file, Xcoff, R>; + type Comdat = XcoffComdat<'data, 'file, Xcoff, R>; + type ComdatIterator = XcoffComdatIterator<'data, 'file, Xcoff, R>; + type Symbol = XcoffSymbol<'data, 'file, Xcoff, R>; + type SymbolIterator = XcoffSymbolIterator<'data, 'file, Xcoff, R>; + type SymbolTable = XcoffSymbolTable<'data, 'file, Xcoff, R>; + type DynamicRelocationIterator = NoDynamicRelocationIterator; + + fn architecture(&self) -> crate::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(&'file self) -> XcoffSegmentIterator<'data, 'file, Xcoff, R> { + XcoffSegmentIterator { file: self } + } + + fn section_by_name_bytes( + &'file self, + section_name: &[u8], + ) -> Option<XcoffSection<'data, 'file, Xcoff, R>> { + self.sections() + .find(|section| section.name_bytes() == Ok(section_name)) + } + + fn section_by_index( + &'file self, + index: SectionIndex, + ) -> Result<XcoffSection<'data, 'file, Xcoff, R>> { + let section = self.sections.section(index)?; + Ok(XcoffSection { + file: self, + section, + index, + }) + } + + fn sections(&'file self) -> XcoffSectionIterator<'data, 'file, Xcoff, R> { + XcoffSectionIterator { + file: self, + iter: self.sections.iter().enumerate(), + } + } + + fn comdats(&'file self) -> XcoffComdatIterator<'data, 'file, Xcoff, R> { + XcoffComdatIterator { file: self } + } + + fn symbol_table(&'file self) -> Option<XcoffSymbolTable<'data, 'file, Xcoff, R>> { + if self.symbols.is_empty() { + return None; + } + Some(XcoffSymbolTable { + symbols: &self.symbols, + file: self, + }) + } + + fn symbol_by_index( + &'file self, + index: SymbolIndex, + ) -> Result<XcoffSymbol<'data, 'file, Xcoff, R>> { + let symbol = self.symbols.symbol(index.0)?; + Ok(XcoffSymbol { + symbols: &self.symbols, + index, + symbol, + file: self, + }) + } + + fn symbols(&'file self) -> XcoffSymbolIterator<'data, 'file, Xcoff, R> { + XcoffSymbolIterator { + symbols: &self.symbols, + index: 0, + file: self, + } + } + + fn dynamic_symbol_table(&'file self) -> Option<XcoffSymbolTable<'data, 'file, Xcoff, R>> { + None + } + + fn dynamic_symbols(&'file self) -> XcoffSymbolIterator<'data, 'file, Xcoff, R> { + // TODO: return the symbols in the STYP_LOADER section. + XcoffSymbolIterator { + file: self, + symbols: &self.symbols, + // Hack: don't return any. + index: self.symbols.len(), + } + } + + fn dynamic_relocations(&'file self) -> Option<Self::DynamicRelocationIterator> { + // TODO: return the relocations in the STYP_LOADER section. + None + } + + fn imports(&self) -> Result<alloc::vec::Vec<crate::Import<'data>>> { + // TODO: return the imports in the STYP_LOADER section. + Ok(Vec::new()) + } + + fn exports(&self) -> Result<alloc::vec::Vec<crate::Export<'data>>> { + // 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(&'file self) -> u64 { + 0 + } + + fn entry(&'file 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 `FileHeader32` and `FileHeader64`. +#[allow(missing_docs)] +pub trait FileHeader: Debug + Pod { + type Word: Into<u64>; + type AuxHeader: AuxHeader<Word = Self::Word>; + type SectionHeader: SectionHeader<Word = Self::Word>; + type Symbol: Symbol<Word = Self::Word>; + type FileAux: FileAux; + type CsectAux: CsectAux; + + /// 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::<Self>(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<Option<&'data Self::AuxHeader>> { + 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::<Self::AuxHeader>() as u16 { + *offset += u64::from(aux_header_size); + return Ok(None); + } + let aux_header = data + .read::<Self::AuxHeader>(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<'data, Self>> { + SectionTable::parse(self, data, offset) + } + + /// Return the symbol table. + #[inline] + fn symbols<'data, R: ReadRef<'data>>(&self, data: R) -> Result<SymbolTable<'data, Self, R>> { + 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; + + 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; + + 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) + } +} + +#[allow(missing_docs)] +pub trait AuxHeader: Debug + Pod { + type Word: Into<u64>; + + 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_sntdata(&self) -> u16; + fn o_sntbss(&self) -> u16; + fn o_algntext(&self) -> u16; + fn o_algndata(&self) -> u16; + fn o_maxstack(&self) -> Self::Word; + fn o_maxdata(&self) -> Self::Word; + fn o_textpsize(&self) -> u8; + fn o_datapsize(&self) -> u8; + fn o_stackpsize(&self) -> u8; +} + +impl AuxHeader for xcoff::AuxHeader32 { + type Word = u32; + + 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_sntdata(&self) -> u16 { + self.o_sntdata.get(BE) + } + + fn o_sntbss(&self) -> u16 { + self.o_sntbss.get(BE) + } + + fn o_algntext(&self) -> u16 { + self.o_algntext.get(BE) + } + + fn o_algndata(&self) -> u16 { + self.o_algndata.get(BE) + } + + fn o_maxstack(&self) -> Self::Word { + self.o_maxstack.get(BE) + } + + fn o_maxdata(&self) -> Self::Word { + self.o_maxdata.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 + } +} + +impl AuxHeader for xcoff::AuxHeader64 { + type Word = u64; + + 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_sntdata(&self) -> u16 { + self.o_sntdata.get(BE) + } + + fn o_sntbss(&self) -> u16 { + self.o_sntbss.get(BE) + } + + fn o_algntext(&self) -> u16 { + self.o_algntext.get(BE) + } + + fn o_algndata(&self) -> u16 { + self.o_algndata.get(BE) + } + + fn o_maxstack(&self) -> Self::Word { + self.o_maxstack.get(BE) + } + + fn o_maxdata(&self) -> Self::Word { + self.o_maxdata.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 + } +} diff --git a/third_party/rust/object/src/read/xcoff/mod.rs b/third_party/rust/object/src/read/xcoff/mod.rs new file mode 100644 index 0000000000..136e31073b --- /dev/null +++ b/third_party/rust/object/src/read/xcoff/mod.rs @@ -0,0 +1,21 @@ +//! Support for reading AIX XCOFF files. +//! +//! Provides `XcoffFile` and related types which implement the `Object` trait. + +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::*; diff --git a/third_party/rust/object/src/read/xcoff/relocation.rs b/third_party/rust/object/src/read/xcoff/relocation.rs new file mode 100644 index 0000000000..78c6acfc7f --- /dev/null +++ b/third_party/rust/object/src/read/xcoff/relocation.rs @@ -0,0 +1,127 @@ +use alloc::fmt; +use core::fmt::Debug; +use core::slice; + +use crate::pod::Pod; +use crate::{xcoff, BigEndian as BE, Relocation}; + +use crate::read::{ReadRef, RelocationEncoding, RelocationKind, RelocationTarget, SymbolIndex}; + +use super::{FileHeader, SectionHeader, XcoffFile}; + +/// An iterator over the relocations in a `XcoffSection32`. +pub type XcoffRelocationIterator32<'data, 'file, R = &'data [u8]> = + XcoffRelocationIterator<'data, 'file, xcoff::FileHeader32, R>; +/// An iterator over the relocations in a `XcoffSection64`. +pub type XcoffRelocationIterator64<'data, 'file, R = &'data [u8]> = + XcoffRelocationIterator<'data, 'file, xcoff::FileHeader64, R>; + +/// An iterator over the relocations in a `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, <<Xcoff as FileHeader>::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::Item> { + self.relocations.next().map(|relocation| { + let encoding = RelocationEncoding::Generic; + let (kind, addend) = match relocation.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), + r_type => (RelocationKind::Xcoff(r_type), 0), + }; + let size = (relocation.r_rsize() & 0x3F) + 1; + let target = RelocationTarget::Symbol(SymbolIndex(relocation.r_symndx() as usize)); + ( + relocation.r_vaddr().into(), + Relocation { + kind, + encoding, + size, + target, + addend, + implicit_addend: true, + }, + ) + }) + } +} + +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 `Rel32` and `Rel64`. +#[allow(missing_docs)] +pub trait Rel: Debug + Pod { + type Word: Into<u64>; + fn r_vaddr(&self) -> Self::Word; + fn r_symndx(&self) -> u32; + fn r_rsize(&self) -> u8; + fn r_rtype(&self) -> u8; +} + +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 + } +} diff --git a/third_party/rust/object/src/read/xcoff/section.rs b/third_party/rust/object/src/read/xcoff/section.rs new file mode 100644 index 0000000000..77453fcd21 --- /dev/null +++ b/third_party/rust/object/src/read/xcoff/section.rs @@ -0,0 +1,427 @@ +use core::fmt::Debug; +use core::{iter, result, slice, str}; + +use crate::{ + xcoff, BigEndian as BE, CompressedData, CompressedFileRange, Pod, SectionFlags, SectionKind, +}; + +use crate::read::{self, Error, ObjectSection, ReadError, ReadRef, Result, SectionIndex}; + +use super::{AuxHeader, FileHeader, Rel, XcoffFile, XcoffRelocationIterator}; + +/// An iterator over the sections of an `XcoffFile32`. +pub type XcoffSectionIterator32<'data, 'file, R = &'data [u8]> = + XcoffSectionIterator<'data, 'file, xcoff::FileHeader32, R>; +/// An iterator over the sections of an `XcoffFile64`. +pub type XcoffSectionIterator64<'data, 'file, R = &'data [u8]> = + XcoffSectionIterator<'data, 'file, xcoff::FileHeader64, R>; + +/// An iterator over the sections of 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<slice::Iter<'data, Xcoff::SectionHeader>>, +} + +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::Item> { + self.iter.next().map(|(index, section)| XcoffSection { + index: SectionIndex(index + 1), + file: self.file, + section, + }) + } +} + +/// A section of an `XcoffFile32`. +pub type XcoffSection32<'data, 'file, R = &'data [u8]> = + XcoffSection<'data, 'file, xcoff::FileHeader32, R>; +/// A section of an `XcoffFile64`. +pub type XcoffSection64<'data, 'file, R = &'data [u8]> = + XcoffSection<'data, 'file, xcoff::FileHeader64, R>; + +/// A section of an `XcoffFile`. +#[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> { + 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<Option<&'data [u8]>> { + Ok(read::util::data_range( + self.bytes()?, + self.address(), + address, + size, + )) + } + + fn compressed_file_range(&self) -> Result<CompressedFileRange> { + Ok(CompressedFileRange::none(self.file_range())) + } + + fn compressed_data(&self) -> Result<CompressedData<'data>> { + self.data().map(CompressedData::none) + } + + fn name_bytes(&self) -> read::Result<&[u8]> { + Ok(self.section.name()) + } + + fn name(&self) -> read::Result<&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<Option<&[u8]>> { + Ok(None) + } + + fn segment_name(&self) -> Result<Option<&str>> { + 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.section.relocations(self.file.data).unwrap_or(&[]); + XcoffRelocationIterator { + file: self.file, + relocations: rel.iter(), + } + } + + fn flags(&self) -> SectionFlags { + SectionFlags::Xcoff { + s_flags: self.section.s_flags(), + } + } + + fn uncompressed_data(&self) -> Result<alloc::borrow::Cow<'data, [u8]>> { + self.compressed_data()?.decompress() + } +} + +/// The table of section headers in an XCOFF file. +#[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<R: ReadRef<'data>>(header: &Xcoff, data: R, offset: &mut u64) -> Result<Self> { + 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 `SectionHeader32` and `SectionHeader64`. +#[allow(missing_docs)] +pub trait SectionHeader: Debug + Pod { + type Word: Into<u64>; + type HalfWord: Into<u32>; + type Xcoff: FileHeader<SectionHeader = Self, Word = Self::Word>; + type Rel: Rel<Word = Self::Word>; + + 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") + } +} diff --git a/third_party/rust/object/src/read/xcoff/segment.rs b/third_party/rust/object/src/read/xcoff/segment.rs new file mode 100644 index 0000000000..7eca72367a --- /dev/null +++ b/third_party/rust/object/src/read/xcoff/segment.rs @@ -0,0 +1,113 @@ +//! 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}; +use crate::xcoff; + +use super::{FileHeader, XcoffFile}; + +/// An iterator over the segments of an `XcoffFile32`. +pub type XcoffSegmentIterator32<'data, 'file, R = &'data [u8]> = + XcoffSegmentIterator<'data, 'file, xcoff::FileHeader32, R>; +/// An iterator over the segments of an `XcoffFile64`. +pub type XcoffSegmentIterator64<'data, 'file, R = &'data [u8]> = + XcoffSegmentIterator<'data, 'file, xcoff::FileHeader64, R>; + +/// An iterator over the segments of an `XcoffFile`. +#[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<Self::Item> { + None + } +} + +/// A segment of an `XcoffFile32`. +pub type XcoffSegment32<'data, 'file, R = &'data [u8]> = + XcoffSegment<'data, 'file, xcoff::FileHeader32, R>; +/// A segment of an `XcoffFile64`. +pub type XcoffSegment64<'data, 'file, R = &'data [u8]> = + XcoffSegment<'data, 'file, xcoff::FileHeader64, R>; + +/// A loadable section of an `XcoffFile`. +#[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<Option<&'data [u8]>> { + unreachable!(); + } + + fn name_bytes(&self) -> Result<Option<&[u8]>> { + unreachable!(); + } + + fn name(&self) -> Result<Option<&str>> { + unreachable!(); + } + + fn flags(&self) -> crate::SegmentFlags { + unreachable!(); + } +} diff --git a/third_party/rust/object/src/read/xcoff/symbol.rs b/third_party/rust/object/src/read/xcoff/symbol.rs new file mode 100644 index 0000000000..7ce215fac0 --- /dev/null +++ b/third_party/rust/object/src/read/xcoff/symbol.rs @@ -0,0 +1,695 @@ +use 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::util::StringTable; +use crate::xcoff; + +use crate::read::{ + self, Bytes, Error, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, Result, SectionIndex, + SymbolFlags, SymbolIndex, SymbolKind, SymbolScope, SymbolSection, +}; + +use super::{FileHeader, XcoffFile}; + +/// A table of symbol entries in an XCOFF file. +/// +/// Also includes the string table used for the symbol names. +#[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<Xcoff>, +} + +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<Self> { + 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::<U32Bytes<_>>(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 symbol entry at the given index and offset. + pub fn get<T: Pod>(&self, index: usize, offset: usize) -> Result<&'data T> { + let entry = index + .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") + } + + /// Return the symbol at the given index. + pub fn symbol(&self, index: usize) -> Result<&'data Xcoff::Symbol> { + self.get::<Xcoff::Symbol>(index, 0) + } + + /// Return a file auxiliary symbol. + pub fn aux_file(&self, index: usize, offset: usize) -> Result<&'data Xcoff::FileAux> { + debug_assert!(self.symbol(index)?.has_aux_file()); + let aux_file = self.get::<Xcoff::FileAux>(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: usize, offset: usize) -> Result<&'data Xcoff::CsectAux> { + debug_assert!(self.symbol(index)?.has_aux_csect()); + let aux_csect = self.get::<Xcoff::CsectAux>(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() + } +} + +/// A symbol table of an `XcoffFile32`. +pub type XcoffSymbolTable32<'data, 'file, R = &'data [u8]> = + XcoffSymbolTable<'data, 'file, xcoff::FileHeader32, R>; +/// A symbol table of an `XcoffFile64`. +pub type XcoffSymbolTable64<'data, 'file, R = &'data [u8]> = + XcoffSymbolTable<'data, 'file, xcoff::FileHeader64, R>; + +/// A symbol table of an `XcoffFile`. +#[derive(Debug, Clone, Copy)] +pub struct XcoffSymbolTable<'data, 'file, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + pub(crate) 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, + index: 0, + } + } + + fn symbol_by_index(&self, index: SymbolIndex) -> read::Result<Self::Symbol> { + let symbol = self.symbols.symbol(index.0)?; + Ok(XcoffSymbol { + file: self.file, + symbols: self.symbols, + index, + symbol, + }) + } +} + +/// An iterator over the symbols of an `XcoffFile32`. +pub type XcoffSymbolIterator32<'data, 'file, R = &'data [u8]> = + XcoffSymbolIterator<'data, 'file, xcoff::FileHeader32, R>; +/// An iterator over the symbols of an `XcoffFile64`. +pub type XcoffSymbolIterator64<'data, 'file, R = &'data [u8]> = + XcoffSymbolIterator<'data, 'file, xcoff::FileHeader64, R>; + +/// An iterator over the symbols of an `XcoffFile`. +pub struct XcoffSymbolIterator<'data, 'file, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + pub(crate) file: &'file XcoffFile<'data, Xcoff, R>, + pub(super) symbols: &'file SymbolTable<'data, Xcoff, R>, + pub(super) index: usize, +} + +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<Self::Item> { + let index = self.index; + let symbol = self.symbols.symbol(index).ok()?; + // TODO: skip over the auxiliary symbols for now. + self.index += 1 + symbol.n_numaux() as usize; + Some(XcoffSymbol { + file: self.file, + symbols: self.symbols, + index: SymbolIndex(index), + symbol, + }) + } +} + +/// A symbol of an `XcoffFile32`. +pub type XcoffSymbol32<'data, 'file, R = &'data [u8]> = + XcoffSymbol<'data, 'file, xcoff::FileHeader32, R>; +/// A symbol of an `XcoffFile64`. +pub type XcoffSymbol64<'data, 'file, R = &'data [u8]> = + XcoffSymbol<'data, 'file, xcoff::FileHeader64, R>; + +/// A symbol of an `XcoffFile`. +#[derive(Debug, Clone, Copy)] +pub struct XcoffSymbol<'data, 'file, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + pub(crate) 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: 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.0, 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.0, self.symbol.n_numaux() as usize) + { + let sym_type = aux_csect.sym_type() & 0x07; + 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.0, self.symbol.n_numaux() as usize) + { + let sym_type = aux_csect.sym_type() & 0x07; + 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_NULL => SymbolKind::Null, + 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.has_aux_csect() { + if let Ok(aux_csect) = self + .symbols + .aux_csect(self.index.0, self.symbol.n_numaux() as usize) + { + let smclas = aux_csect.x_smclas(); + self.symbol.n_scnum() != xcoff::N_UNDEF + && (smclas == xcoff::XMC_PR + || smclas == xcoff::XMC_RW + || smclas == xcoff::XMC_RO) + } 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 | xcoff::C_HIDEXT => { + 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<SectionIndex, SymbolIndex> { + 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.0, self.symbol.n_numaux() as usize) + { + x_smtyp = aux_csect.x_smtyp(); + x_smclas = aux_csect.x_smclas(); + if x_smtyp == 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 `Symbol32` and `Symbol64`. +#[allow(missing_docs)] +pub trait Symbol: Debug + Pod { + type Word: Into<u64>; + + 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<'data, R: ReadRef<'data>>( + &'data self, + strings: StringTable<'data, R>, + ) -> Result<&'data [u8]>; + + /// 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 + } + + /// 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 + } + + /// Parse the symbol name for XCOFF32. + fn name<'data, R: ReadRef<'data>>( + &'data self, + strings: StringTable<'data, R>, + ) -> Result<&'data [u8]> { + if self.n_name[0] == 0 { + // If the name starts with 0 then the last 4 bytes are a string table offset. + let offset = u32::from_be_bytes(self.n_name[4..8].try_into().unwrap()); + 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 `FileAux32` and `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<u8>; + + /// 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]> { + let x_fname = self.x_fname(); + if x_fname[0] == 0 { + // If the name starts with 0 then the last 4 bytes are a string table offset. + let offset = u32::from_be_bytes(x_fname[4..8].try_into().unwrap()); + 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', 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<u8> { + 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<u8> { + None + } +} + +/// A trait for generic access to `CsectAux32` and `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_auxtype(&self) -> Option<u8>; + + 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_auxtype(&self) -> Option<u8> { + 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_auxtype(&self) -> Option<u8> { + None + } +} diff --git a/third_party/rust/object/src/write/coff.rs b/third_party/rust/object/src/write/coff.rs new file mode 100644 index 0000000000..d2f7ccc640 --- /dev/null +++ b/third_party/rust/object/src/write/coff.rs @@ -0,0 +1,725 @@ +use alloc::vec::Vec; +use core::mem; + +use crate::endian::{LittleEndian as LE, U16Bytes, U32Bytes, U16, U32}; +use crate::pe as coff; +use crate::write::string::*; +use crate::write::util::*; +use crate::write::*; + +#[derive(Default, Clone, Copy)] +struct SectionOffsets { + offset: usize, + str_id: Option<StringId>, + reloc_offset: usize, + selection: u8, + associative_section: u16, +} + +#[derive(Default, Clone, Copy)] +struct SymbolOffsets { + index: usize, + str_id: Option<StringId>, + 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<u8> { + let mut name = section.to_vec(); + name.push(b'$'); + name.extend_from_slice(value); + name + } + + pub(crate) fn coff_fixup_relocation(&mut self, mut relocation: &mut Relocation) -> i64 { + if relocation.kind == RelocationKind::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? + relocation.kind = RelocationKind::Relative; + relocation.symbol = self.coff_add_stub_symbol(relocation.symbol); + } else if relocation.kind == RelocationKind::PltRelative { + // Windows doesn't need a separate relocation type for + // references to functions in import libraries. + // For convenience, treat this the same as Relative. + relocation.kind = RelocationKind::Relative; + } + + let constant = match self.architecture { + Architecture::I386 | Architecture::Arm | Architecture::Aarch64 => match relocation.kind + { + RelocationKind::Relative => { + // IMAGE_REL_I386_REL32, IMAGE_REL_ARM_REL32, IMAGE_REL_ARM64_REL32 + relocation.addend + 4 + } + _ => relocation.addend, + }, + Architecture::X86_64 => match relocation.kind { + RelocationKind::Relative => { + // IMAGE_REL_AMD64_REL32 through to IMAGE_REL_AMD64_REL32_5 + if relocation.addend <= -4 && relocation.addend >= -9 { + 0 + } else { + relocation.addend + 4 + } + } + _ => relocation.addend, + }, + _ => unimplemented!(), + }; + relocation.addend -= constant; + constant + } + + fn coff_add_stub_symbol(&mut self, symbol_id: SymbolId) -> SymbolId { + if let Some(stub_id) = self.stub_symbols.get(&symbol_id) { + return *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)); + section.relocations = vec![Relocation { + offset: 0, + size: stub_size * 8, + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + symbol: symbol_id, + addend: 0, + }]; + + 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); + + 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<()> { + // Calculate offsets of everything, and build strtab. + let mut offset = 0; + let mut strtab = StringTable::default(); + + // COFF header. + offset += mem::size_of::<coff::ImageFileHeader>(); + + // Section headers. + offset += self.sections.len() * mem::size_of::<coff::ImageSectionHeader>(); + + // Calculate size of section data and add section strings to strtab. + let mut section_offsets = vec![SectionOffsets::default(); self.sections.len()]; + for (index, section) in self.sections.iter().enumerate() { + if section.name.len() > 8 { + section_offsets[index].str_id = Some(strtab.add(§ion.name)); + } + + let len = section.data.len(); + if len != 0 { + // TODO: not sure what alignment is required here, but this seems to match LLVM + offset = align(offset, 4); + section_offsets[index].offset = offset; + offset += len; + } else { + section_offsets[index].offset = 0; + } + + // Calculate size of relocations. + let mut count = section.relocations.len(); + if count != 0 { + section_offsets[index].reloc_offset = offset; + if count > 0xffff { + count += 1; + } + offset += count * mem::size_of::<coff::ImageRelocation>(); + } + } + + // 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 u16 + 1; + } + } + } + + // Calculate size of symbols and add symbol strings to strtab. + 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; + match symbol.kind { + SymbolKind::File => { + // Name goes in auxiliary symbol records. + let aux_count = (symbol.name.len() + coff::IMAGE_SIZEOF_SYMBOL - 1) + / coff::IMAGE_SIZEOF_SYMBOL; + symbol_offsets[index].aux_count = aux_count as u8; + symtab_count += aux_count; + // Don't add name to strtab. + continue; + } + SymbolKind::Section => { + symbol_offsets[index].aux_count = 1; + symtab_count += 1; + } + _ => {} + } + if symbol.name.len() > 8 { + symbol_offsets[index].str_id = Some(strtab.add(&symbol.name)); + } + } + + // Calculate size of symtab. + let symtab_offset = offset; + let symtab_len = symtab_count * coff::IMAGE_SIZEOF_SYMBOL; + 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. + let header = coff::ImageFileHeader { + machine: U16::new( + LE, + match self.architecture { + Architecture::Arm => coff::IMAGE_FILE_MACHINE_ARMNT, + Architecture::Aarch64 => coff::IMAGE_FILE_MACHINE_ARM64, + Architecture::I386 => coff::IMAGE_FILE_MACHINE_I386, + Architecture::X86_64 => coff::IMAGE_FILE_MACHINE_AMD64, + _ => { + return Err(Error(format!( + "unimplemented architecture {:?}", + self.architecture + ))); + } + }, + ), + number_of_sections: U16::new(LE, self.sections.len() as u16), + time_date_stamp: U32::default(), + pointer_to_symbol_table: U32::new(LE, symtab_offset as u32), + number_of_symbols: U32::new(LE, symtab_count as u32), + size_of_optional_header: U16::default(), + characteristics: match self.flags { + FileFlags::Coff { characteristics } => U16::new(LE, characteristics), + _ => U16::default(), + }, + }; + buffer.write(&header); + + // 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::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 + ))); + } + }; + let mut coff_section = coff::ImageSectionHeader { + name: [0; 8], + virtual_size: U32::default(), + virtual_address: U32::default(), + size_of_raw_data: U32::new(LE, section.size as u32), + pointer_to_raw_data: U32::new(LE, section_offsets[index].offset as u32), + pointer_to_relocations: U32::new(LE, section_offsets[index].reloc_offset as u32), + pointer_to_linenumbers: U32::default(), + number_of_relocations: if section.relocations.len() > 0xffff { + U16::new(LE, 0xffff) + } else { + U16::new(LE, section.relocations.len() as u16) + }, + number_of_linenumbers: U16::default(), + characteristics: U32::new(LE, characteristics), + }; + if section.name.len() <= 8 { + coff_section.name[..section.name.len()].copy_from_slice(§ion.name); + } else { + let mut str_offset = strtab.get_offset(section_offsets[index].str_id.unwrap()); + 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 if 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; + } + } else { + return Err(Error(format!("invalid section name offset {}", str_offset))); + } + } + buffer.write(&coff_section); + } + + // Write section data and relocations. + 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].offset, buffer.len()); + buffer.write_bytes(§ion.data); + } + + if !section.relocations.is_empty() { + debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len()); + if section.relocations.len() > 0xffff { + let coff_relocation = coff::ImageRelocation { + virtual_address: U32Bytes::new(LE, section.relocations.len() as u32 + 1), + symbol_table_index: U32Bytes::new(LE, 0), + typ: U16Bytes::new(LE, 0), + }; + buffer.write(&coff_relocation); + } + for reloc in §ion.relocations { + //assert!(reloc.implicit_addend); + let typ = match self.architecture { + Architecture::I386 => match (reloc.kind, reloc.size, reloc.addend) { + (RelocationKind::Absolute, 16, 0) => coff::IMAGE_REL_I386_DIR16, + (RelocationKind::Relative, 16, 0) => coff::IMAGE_REL_I386_REL16, + (RelocationKind::Absolute, 32, 0) => coff::IMAGE_REL_I386_DIR32, + (RelocationKind::ImageOffset, 32, 0) => coff::IMAGE_REL_I386_DIR32NB, + (RelocationKind::SectionIndex, 16, 0) => coff::IMAGE_REL_I386_SECTION, + (RelocationKind::SectionOffset, 32, 0) => coff::IMAGE_REL_I386_SECREL, + (RelocationKind::SectionOffset, 7, 0) => coff::IMAGE_REL_I386_SECREL7, + (RelocationKind::Relative, 32, -4) => coff::IMAGE_REL_I386_REL32, + (RelocationKind::Coff(x), _, _) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + Architecture::X86_64 => match (reloc.kind, reloc.size, reloc.addend) { + (RelocationKind::Absolute, 64, 0) => coff::IMAGE_REL_AMD64_ADDR64, + (RelocationKind::Absolute, 32, 0) => coff::IMAGE_REL_AMD64_ADDR32, + (RelocationKind::ImageOffset, 32, 0) => coff::IMAGE_REL_AMD64_ADDR32NB, + (RelocationKind::Relative, 32, -4) => coff::IMAGE_REL_AMD64_REL32, + (RelocationKind::Relative, 32, -5) => coff::IMAGE_REL_AMD64_REL32_1, + (RelocationKind::Relative, 32, -6) => coff::IMAGE_REL_AMD64_REL32_2, + (RelocationKind::Relative, 32, -7) => coff::IMAGE_REL_AMD64_REL32_3, + (RelocationKind::Relative, 32, -8) => coff::IMAGE_REL_AMD64_REL32_4, + (RelocationKind::Relative, 32, -9) => coff::IMAGE_REL_AMD64_REL32_5, + (RelocationKind::SectionIndex, 16, 0) => coff::IMAGE_REL_AMD64_SECTION, + (RelocationKind::SectionOffset, 32, 0) => coff::IMAGE_REL_AMD64_SECREL, + (RelocationKind::SectionOffset, 7, 0) => coff::IMAGE_REL_AMD64_SECREL7, + (RelocationKind::Coff(x), _, _) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + Architecture::Arm => match (reloc.kind, reloc.size, reloc.addend) { + (RelocationKind::Absolute, 32, 0) => coff::IMAGE_REL_ARM_ADDR32, + (RelocationKind::ImageOffset, 32, 0) => coff::IMAGE_REL_ARM_ADDR32NB, + (RelocationKind::Relative, 32, -4) => coff::IMAGE_REL_ARM_REL32, + (RelocationKind::SectionIndex, 16, 0) => coff::IMAGE_REL_ARM_SECTION, + (RelocationKind::SectionOffset, 32, 0) => coff::IMAGE_REL_ARM_SECREL, + (RelocationKind::Coff(x), _, _) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + Architecture::Aarch64 => match (reloc.kind, reloc.size, reloc.addend) { + (RelocationKind::Absolute, 32, 0) => coff::IMAGE_REL_ARM64_ADDR32, + (RelocationKind::ImageOffset, 32, 0) => coff::IMAGE_REL_ARM64_ADDR32NB, + (RelocationKind::SectionIndex, 16, 0) => coff::IMAGE_REL_ARM64_SECTION, + (RelocationKind::SectionOffset, 32, 0) => coff::IMAGE_REL_ARM64_SECREL, + (RelocationKind::Absolute, 64, 0) => coff::IMAGE_REL_ARM64_ADDR64, + (RelocationKind::Relative, 32, -4) => coff::IMAGE_REL_ARM64_REL32, + (RelocationKind::Coff(x), _, _) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + _ => { + return Err(Error(format!( + "unimplemented architecture {:?}", + self.architecture + ))); + } + }; + let coff_relocation = coff::ImageRelocation { + virtual_address: U32Bytes::new(LE, reloc.offset as u32), + symbol_table_index: U32Bytes::new( + LE, + symbol_offsets[reloc.symbol.0].index as u32, + ), + typ: U16Bytes::new(LE, typ), + }; + buffer.write(&coff_relocation); + } + } + } + + // Write symbols. + debug_assert_eq!(symtab_offset, buffer.len()); + for (index, symbol) in self.symbols.iter().enumerate() { + let mut name = &symbol.name[..]; + 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 => { + // Name goes in auxiliary symbol records. + name = b".file"; + coff::IMAGE_SYM_CLASS_FILE + } + SymbolKind::Section => coff::IMAGE_SYM_CLASS_STATIC, + 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 | SymbolKind::Null => { + 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 + }; + let mut coff_symbol = coff::ImageSymbol { + name: [0; 8], + value: U32Bytes::new(LE, value), + section_number: U16Bytes::new(LE, section_number), + typ: U16Bytes::new(LE, typ), + storage_class, + number_of_aux_symbols, + }; + if name.len() <= 8 { + coff_symbol.name[..name.len()].copy_from_slice(name); + } else { + let str_offset = strtab.get_offset(symbol_offsets[index].str_id.unwrap()); + coff_symbol.name[4..8].copy_from_slice(&u32::to_le_bytes(str_offset as u32)); + } + buffer.write(&coff_symbol); + + // Write auxiliary symbols. + match symbol.kind { + SymbolKind::File => { + let aux_len = number_of_aux_symbols as usize * coff::IMAGE_SIZEOF_SYMBOL; + debug_assert!(aux_len >= symbol.name.len()); + let old_len = buffer.len(); + buffer.write_bytes(&symbol.name); + buffer.resize(old_len + aux_len); + } + SymbolKind::Section => { + debug_assert_eq!(number_of_aux_symbols, 1); + let section_index = symbol.section.id().unwrap().0; + let section = &self.sections[section_index]; + let aux = coff::ImageAuxSymbolSection { + length: U32Bytes::new(LE, section.size as u32), + number_of_relocations: if section.relocations.len() > 0xffff { + U16Bytes::new(LE, 0xffff) + } else { + U16Bytes::new(LE, section.relocations.len() as u16) + }, + number_of_linenumbers: U16Bytes::default(), + check_sum: U32Bytes::new(LE, checksum(section.data())), + number: U16Bytes::new( + LE, + section_offsets[section_index].associative_section, + ), + selection: section_offsets[section_index].selection, + reserved: 0, + // TODO: bigobj + high_number: U16Bytes::default(), + }; + buffer.write(&aux); + } + _ => { + debug_assert_eq!(number_of_aux_symbols, 0); + } + } + } + + // Write strtab section. + debug_assert_eq!(strtab_offset, buffer.len()); + buffer.write_bytes(&u32::to_le_bytes(strtab_len as u32)); + buffer.write_bytes(&strtab_data); + + debug_assert_eq!(offset, buffer.len()); + + Ok(()) + } +} + +// JamCRC +fn checksum(data: &[u8]) -> u32 { + let mut hasher = crc32fast::Hasher::new_with_initial(0xffff_ffff); + hasher.update(data); + !hasher.finalize() +} diff --git a/third_party/rust/object/src/write/elf/mod.rs b/third_party/rust/object/src/write/elf/mod.rs new file mode 100644 index 0000000000..3a4f3716e8 --- /dev/null +++ b/third_party/rust/object/src/write/elf/mod.rs @@ -0,0 +1,9 @@ +//! 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::*; diff --git a/third_party/rust/object/src/write/elf/object.rs b/third_party/rust/object/src/write/elf/object.rs new file mode 100644 index 0000000000..acc820c9ec --- /dev/null +++ b/third_party/rust/object/src/write/elf/object.rs @@ -0,0 +1,903 @@ +use alloc::vec::Vec; + +use crate::write::elf::writer::*; +use crate::write::string::StringId; +use crate::write::*; +use crate::AddressSize; +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<StringId>, +} + +#[derive(Default, Clone, Copy)] +struct SymbolOffsets { + index: SymbolIndex, + str_id: Option<StringId>, +} + +// 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<u8> { + let mut name = section.to_vec(); + name.push(b'.'); + name.extend_from_slice(value); + name + } + + fn elf_has_relocation_addend(&self) -> Result<bool> { + Ok(match self.architecture { + Architecture::Aarch64 => true, + Architecture::Aarch64_Ilp32 => true, + Architecture::Arm => false, + Architecture::Avr => true, + Architecture::Bpf => false, + Architecture::Csky => 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::Sparc64 => true, + Architecture::Xtensa => true, + _ => { + return Err(Error(format!( + "unimplemented architecture {:?}", + self.architecture + ))); + } + }) + } + + pub(crate) fn elf_fixup_relocation(&mut self, mut relocation: &mut Relocation) -> Result<i64> { + // Return true if we should use a section symbol to avoid preemption. + fn want_section_symbol(relocation: &Relocation, symbol: &Symbol) -> bool { + if symbol.scope != SymbolScope::Dynamic { + // Only dynamic symbols can be preemptible. + return false; + } + match symbol.kind { + SymbolKind::Text | SymbolKind::Data => {} + _ => return false, + } + match relocation.kind { + // Anything using GOT or PLT is preemptible. + // We also require that `Other` relocations must already be correct. + RelocationKind::Got + | RelocationKind::GotRelative + | RelocationKind::GotBaseRelative + | RelocationKind::PltRelative + | RelocationKind::Elf(_) => return false, + // Absolute relocations are preemptible for non-local data. + // TODO: not sure if this rule is exactly correct + // This rule was added to handle global data references in debuginfo. + // Maybe this should be a new relocation kind so that the caller can decide. + RelocationKind::Absolute => { + if symbol.kind == SymbolKind::Data { + return false; + } + } + _ => {} + } + true + } + + // Use section symbols for relocations where required to avoid preemption. + // Otherwise, the linker will fail with: + // relocation R_X86_64_PC32 against symbol `SomeSymbolName' can not be used when + // making a shared object; recompile with -fPIC + let symbol = &self.symbols[relocation.symbol.0]; + if want_section_symbol(relocation, symbol) { + if let Some(section) = symbol.section.id() { + relocation.addend += symbol.value as i64; + relocation.symbol = self.section_symbol(section); + } + } + + // Determine whether the addend is stored in the relocation or the data. + if self.elf_has_relocation_addend()? { + Ok(0) + } else { + let constant = relocation.addend; + relocation.addend = 0; + Ok(constant) + } + } + + 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 { + Architecture::Aarch64 => elf::EM_AARCH64, + Architecture::Aarch64_Ilp32 => elf::EM_AARCH64, + Architecture::Arm => elf::EM_ARM, + Architecture::Avr => elf::EM_AVR, + Architecture::Bpf => elf::EM_BPF, + Architecture::Csky => elf::EM_CSKY, + Architecture::I386 => elf::EM_386, + Architecture::X86_64 => elf::EM_X86_64, + Architecture::X86_64_X32 => elf::EM_X86_64, + Architecture::Hexagon => elf::EM_HEXAGON, + Architecture::LoongArch64 => elf::EM_LOONGARCH, + Architecture::Mips => elf::EM_MIPS, + Architecture::Mips64 => elf::EM_MIPS, + Architecture::Msp430 => elf::EM_MSP430, + Architecture::PowerPc => elf::EM_PPC, + Architecture::PowerPc64 => elf::EM_PPC64, + Architecture::Riscv32 => elf::EM_RISCV, + Architecture::Riscv64 => elf::EM_RISCV, + Architecture::S390x => elf::EM_S390, + Architecture::Sbf => elf::EM_SBF, + Architecture::Sparc64 => elf::EM_SPARCV9, + Architecture::Xtensa => elf::EM_XTENSA, + _ => { + return Err(Error(format!( + "unimplemented architecture {:?}", + self.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::Null => elf::STT_NOTYPE, + 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 = match self.architecture { + Architecture::Aarch64 => match (reloc.kind, reloc.encoding, reloc.size) { + (RelocationKind::Absolute, RelocationEncoding::Generic, 64) => { + elf::R_AARCH64_ABS64 + } + (RelocationKind::Absolute, RelocationEncoding::Generic, 32) => { + elf::R_AARCH64_ABS32 + } + (RelocationKind::Absolute, RelocationEncoding::Generic, 16) => { + elf::R_AARCH64_ABS16 + } + (RelocationKind::Relative, RelocationEncoding::Generic, 64) => { + elf::R_AARCH64_PREL64 + } + (RelocationKind::Relative, RelocationEncoding::Generic, 32) => { + elf::R_AARCH64_PREL32 + } + (RelocationKind::Relative, RelocationEncoding::Generic, 16) => { + elf::R_AARCH64_PREL16 + } + (RelocationKind::Relative, RelocationEncoding::AArch64Call, 26) + | (RelocationKind::PltRelative, RelocationEncoding::AArch64Call, 26) => { + elf::R_AARCH64_CALL26 + } + (RelocationKind::Elf(x), _, _) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + Architecture::Aarch64_Ilp32 => { + match (reloc.kind, reloc.encoding, reloc.size) { + (RelocationKind::Absolute, RelocationEncoding::Generic, 32) => { + elf::R_AARCH64_P32_ABS32 + } + (RelocationKind::Elf(x), _, _) => x, + _ => { + return Err(Error(format!( + "unimplemented relocation {:?}", + reloc + ))); + } + } + } + Architecture::Arm => match (reloc.kind, reloc.encoding, reloc.size) { + (RelocationKind::Absolute, _, 32) => elf::R_ARM_ABS32, + (RelocationKind::Elf(x), _, _) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + Architecture::Avr => match (reloc.kind, reloc.encoding, reloc.size) { + (RelocationKind::Absolute, _, 32) => elf::R_AVR_32, + (RelocationKind::Absolute, _, 16) => elf::R_AVR_16, + (RelocationKind::Elf(x), _, _) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + Architecture::Bpf => match (reloc.kind, reloc.encoding, reloc.size) { + (RelocationKind::Absolute, _, 64) => elf::R_BPF_64_64, + (RelocationKind::Absolute, _, 32) => elf::R_BPF_64_32, + (RelocationKind::Elf(x), _, _) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + Architecture::Csky => match (reloc.kind, reloc.encoding, reloc.size) { + (RelocationKind::Absolute, _, 32) => elf::R_CKCORE_ADDR32, + (RelocationKind::Relative, RelocationEncoding::Generic, 32) => { + elf::R_CKCORE_PCREL32 + } + (RelocationKind::Elf(x), _, _) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + Architecture::I386 => match (reloc.kind, reloc.size) { + (RelocationKind::Absolute, 32) => elf::R_386_32, + (RelocationKind::Relative, 32) => elf::R_386_PC32, + (RelocationKind::Got, 32) => elf::R_386_GOT32, + (RelocationKind::PltRelative, 32) => elf::R_386_PLT32, + (RelocationKind::GotBaseOffset, 32) => elf::R_386_GOTOFF, + (RelocationKind::GotBaseRelative, 32) => elf::R_386_GOTPC, + (RelocationKind::Absolute, 16) => elf::R_386_16, + (RelocationKind::Relative, 16) => elf::R_386_PC16, + (RelocationKind::Absolute, 8) => elf::R_386_8, + (RelocationKind::Relative, 8) => elf::R_386_PC8, + (RelocationKind::Elf(x), _) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + Architecture::X86_64 | Architecture::X86_64_X32 => { + match (reloc.kind, reloc.encoding, reloc.size) { + (RelocationKind::Absolute, RelocationEncoding::Generic, 64) => { + elf::R_X86_64_64 + } + (RelocationKind::Relative, _, 32) => elf::R_X86_64_PC32, + (RelocationKind::Got, _, 32) => elf::R_X86_64_GOT32, + (RelocationKind::PltRelative, _, 32) => elf::R_X86_64_PLT32, + (RelocationKind::GotRelative, _, 32) => elf::R_X86_64_GOTPCREL, + (RelocationKind::Absolute, RelocationEncoding::Generic, 32) => { + elf::R_X86_64_32 + } + (RelocationKind::Absolute, RelocationEncoding::X86Signed, 32) => { + elf::R_X86_64_32S + } + (RelocationKind::Absolute, _, 16) => elf::R_X86_64_16, + (RelocationKind::Relative, _, 16) => elf::R_X86_64_PC16, + (RelocationKind::Absolute, _, 8) => elf::R_X86_64_8, + (RelocationKind::Relative, _, 8) => elf::R_X86_64_PC8, + (RelocationKind::Elf(x), _, _) => x, + _ => { + return Err(Error(format!( + "unimplemented relocation {:?}", + reloc + ))); + } + } + } + Architecture::Hexagon => match (reloc.kind, reloc.encoding, reloc.size) { + (RelocationKind::Absolute, _, 32) => elf::R_HEX_32, + (RelocationKind::Elf(x), _, _) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + Architecture::LoongArch64 => match (reloc.kind, reloc.encoding, reloc.size) + { + (RelocationKind::Absolute, _, 32) => elf::R_LARCH_32, + (RelocationKind::Absolute, _, 64) => elf::R_LARCH_64, + (RelocationKind::Relative, _, 32) => elf::R_LARCH_32_PCREL, + (RelocationKind::Relative, RelocationEncoding::LoongArchBranch, 16) + | ( + RelocationKind::PltRelative, + RelocationEncoding::LoongArchBranch, + 16, + ) => elf::R_LARCH_B16, + (RelocationKind::Relative, RelocationEncoding::LoongArchBranch, 21) + | ( + RelocationKind::PltRelative, + RelocationEncoding::LoongArchBranch, + 21, + ) => elf::R_LARCH_B21, + (RelocationKind::Relative, RelocationEncoding::LoongArchBranch, 26) + | ( + RelocationKind::PltRelative, + RelocationEncoding::LoongArchBranch, + 26, + ) => elf::R_LARCH_B26, + (RelocationKind::Elf(x), _, _) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + Architecture::Mips | Architecture::Mips64 => { + match (reloc.kind, reloc.encoding, reloc.size) { + (RelocationKind::Absolute, _, 16) => elf::R_MIPS_16, + (RelocationKind::Absolute, _, 32) => elf::R_MIPS_32, + (RelocationKind::Absolute, _, 64) => elf::R_MIPS_64, + (RelocationKind::Elf(x), _, _) => x, + _ => { + return Err(Error(format!( + "unimplemented relocation {:?}", + reloc + ))); + } + } + } + Architecture::Msp430 => match (reloc.kind, reloc.encoding, reloc.size) { + (RelocationKind::Absolute, _, 32) => elf::R_MSP430_32, + (RelocationKind::Absolute, _, 16) => elf::R_MSP430_16_BYTE, + (RelocationKind::Elf(x), _, _) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + Architecture::PowerPc => match (reloc.kind, reloc.encoding, reloc.size) { + (RelocationKind::Absolute, _, 32) => elf::R_PPC_ADDR32, + (RelocationKind::Elf(x), _, _) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + Architecture::PowerPc64 => match (reloc.kind, reloc.encoding, reloc.size) { + (RelocationKind::Absolute, _, 32) => elf::R_PPC64_ADDR32, + (RelocationKind::Absolute, _, 64) => elf::R_PPC64_ADDR64, + (RelocationKind::Elf(x), _, _) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + Architecture::Riscv32 | Architecture::Riscv64 => { + match (reloc.kind, reloc.encoding, reloc.size) { + (RelocationKind::Absolute, _, 32) => elf::R_RISCV_32, + (RelocationKind::Absolute, _, 64) => elf::R_RISCV_64, + (RelocationKind::Relative, RelocationEncoding::Generic, 32) => { + elf::R_RISCV_32_PCREL + } + (RelocationKind::Elf(x), _, _) => x, + _ => { + return Err(Error(format!( + "unimplemented relocation {:?}", + reloc + ))); + } + } + } + Architecture::S390x => match (reloc.kind, reloc.encoding, reloc.size) { + (RelocationKind::Absolute, RelocationEncoding::Generic, 8) => { + elf::R_390_8 + } + (RelocationKind::Absolute, RelocationEncoding::Generic, 16) => { + elf::R_390_16 + } + (RelocationKind::Absolute, RelocationEncoding::Generic, 32) => { + elf::R_390_32 + } + (RelocationKind::Absolute, RelocationEncoding::Generic, 64) => { + elf::R_390_64 + } + (RelocationKind::Relative, RelocationEncoding::Generic, 16) => { + elf::R_390_PC16 + } + (RelocationKind::Relative, RelocationEncoding::Generic, 32) => { + elf::R_390_PC32 + } + (RelocationKind::Relative, RelocationEncoding::Generic, 64) => { + elf::R_390_PC64 + } + (RelocationKind::Relative, RelocationEncoding::S390xDbl, 16) => { + elf::R_390_PC16DBL + } + (RelocationKind::Relative, RelocationEncoding::S390xDbl, 32) => { + elf::R_390_PC32DBL + } + (RelocationKind::PltRelative, RelocationEncoding::S390xDbl, 16) => { + elf::R_390_PLT16DBL + } + (RelocationKind::PltRelative, RelocationEncoding::S390xDbl, 32) => { + elf::R_390_PLT32DBL + } + (RelocationKind::Got, RelocationEncoding::Generic, 16) => { + elf::R_390_GOT16 + } + (RelocationKind::Got, RelocationEncoding::Generic, 32) => { + elf::R_390_GOT32 + } + (RelocationKind::Got, RelocationEncoding::Generic, 64) => { + elf::R_390_GOT64 + } + (RelocationKind::GotRelative, RelocationEncoding::S390xDbl, 32) => { + elf::R_390_GOTENT + } + (RelocationKind::GotBaseOffset, RelocationEncoding::Generic, 16) => { + elf::R_390_GOTOFF16 + } + (RelocationKind::GotBaseOffset, RelocationEncoding::Generic, 32) => { + elf::R_390_GOTOFF32 + } + (RelocationKind::GotBaseOffset, RelocationEncoding::Generic, 64) => { + elf::R_390_GOTOFF64 + } + (RelocationKind::GotBaseRelative, RelocationEncoding::Generic, 64) => { + elf::R_390_GOTPC + } + (RelocationKind::GotBaseRelative, RelocationEncoding::S390xDbl, 32) => { + elf::R_390_GOTPCDBL + } + (RelocationKind::Elf(x), _, _) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + Architecture::Sbf => match (reloc.kind, reloc.encoding, reloc.size) { + (RelocationKind::Absolute, _, 64) => elf::R_SBF_64_64, + (RelocationKind::Absolute, _, 32) => elf::R_SBF_64_32, + (RelocationKind::Elf(x), _, _) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + Architecture::Sparc64 => match (reloc.kind, reloc.encoding, reloc.size) { + // TODO: use R_SPARC_32/R_SPARC_64 if aligned. + (RelocationKind::Absolute, _, 32) => elf::R_SPARC_UA32, + (RelocationKind::Absolute, _, 64) => elf::R_SPARC_UA64, + (RelocationKind::Elf(x), _, _) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + Architecture::Xtensa => match (reloc.kind, reloc.encoding, reloc.size) { + (RelocationKind::Absolute, _, 32) => elf::R_XTENSA_32, + (RelocationKind::Relative, RelocationEncoding::Generic, 32) => { + elf::R_XTENSA_32_PCREL + } + (RelocationKind::Elf(x), _, _) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + _ => { + if let RelocationKind::Elf(x) = reloc.kind { + x + } else { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + } + }; + 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 => 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(()) + } +} diff --git a/third_party/rust/object/src/write/elf/writer.rs b/third_party/rust/object/src/write/elf/writer.rs new file mode 100644 index 0000000000..9750924969 --- /dev/null +++ b/third_party/rust/object/src/write/elf/writer.rs @@ -0,0 +1,2143 @@ +//! 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}; + +/// 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<StringId>, + shstrtab_index: SectionIndex, + shstrtab_offset: usize, + shstrtab_data: Vec<u8>, + + need_strtab: bool, + strtab: StringTable<'a>, + strtab_str_id: Option<StringId>, + strtab_index: SectionIndex, + strtab_offset: usize, + strtab_data: Vec<u8>, + + symtab_str_id: Option<StringId>, + symtab_index: SectionIndex, + symtab_offset: usize, + symtab_num: u32, + + need_symtab_shndx: bool, + symtab_shndx_str_id: Option<StringId>, + symtab_shndx_offset: usize, + symtab_shndx_data: Vec<u8>, + + need_dynstr: bool, + dynstr: StringTable<'a>, + dynstr_str_id: Option<StringId>, + dynstr_index: SectionIndex, + dynstr_offset: usize, + dynstr_data: Vec<u8>, + + dynsym_str_id: Option<StringId>, + dynsym_index: SectionIndex, + dynsym_offset: usize, + dynsym_num: u32, + + dynamic_str_id: Option<StringId>, + dynamic_offset: usize, + dynamic_num: usize, + + hash_str_id: Option<StringId>, + hash_offset: usize, + hash_size: usize, + + gnu_hash_str_id: Option<StringId>, + gnu_hash_offset: usize, + gnu_hash_size: usize, + + gnu_versym_str_id: Option<StringId>, + gnu_versym_offset: usize, + + gnu_verdef_str_id: Option<StringId>, + 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<StringId>, + 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<StringId>, + 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, + } + } + + /// 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. + 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); + } + + fn file_header_size(&self) -> usize { + if self.is_64 { + mem::size_of::<elf::FileHeader64<Endianness>>() + } else { + mem::size_of::<elf::FileHeader32<Endianness>>() + } + } + + /// 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.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.file_header_size() as u16; + + let e_phoff = self.segment_offset as u64; + let e_phentsize = if self.segment_num == 0 { + 0 + } else { + self.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.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(()) + } + + fn program_header_size(&self) -> usize { + if self.is_64 { + mem::size_of::<elf::ProgramHeader64<Endianness>>() + } else { + mem::size_of::<elf::ProgramHeader32<Endianness>>() + } + } + + /// 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.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) + } + + fn section_header_size(&self) -> usize { + if self.is_64 { + mem::size_of::<elf::SectionHeader64<Endianness>>() + } else { + mem::size_of::<elf::SectionHeader32<Endianness>>() + } + } + + /// 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.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 { + debug_assert_eq!(self.shstrtab_index, SectionIndex(0)); + self.shstrtab_str_id = Some(self.add_section_name(&b".shstrtab"[..])); + 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 + } + + /// Reserve the range for the string table. + /// + /// This range is used for a section named `.strtab`. + /// + /// This function does nothing if no strings or symbols were defined. + /// 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. + /// + /// This must be called before [`Self::reserve_section_headers`]. + pub fn reserve_strtab_section_index(&mut self) -> SectionIndex { + debug_assert_eq!(self.strtab_index, SectionIndex(0)); + self.strtab_str_id = Some(self.add_section_name(&b".strtab"[..])); + 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<SectionIndex>) -> 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 + } + + fn symbol_size(&self) -> usize { + if self.is_64 { + mem::size_of::<elf::Sym64<Endianness>>() + } else { + mem::size_of::<elf::Sym32<Endianness>>() + } + } + + /// 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.symbol_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::<Endianness>::default()); + } else { + self.buffer.write(&elf::Sym32::<Endianness>::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 { + debug_assert_eq!(self.symtab_index, SectionIndex(0)); + self.symtab_str_id = Some(self.add_section_name(&b".symtab"[..])); + 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.symbol_size() as u64, + sh_link: self.strtab_index.0, + sh_info: num_local, + sh_addralign: self.elf_align as u64, + sh_entsize: self.symbol_size() as u64, + }); + } + + /// Return true if `.symtab_shndx` is needed. + pub fn symtab_shndx_needed(&self) -> bool { + self.need_symtab_shndx + } + + /// 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, 4); + 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; + } + 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 { + debug_assert!(self.symtab_shndx_str_id.is_none()); + self.symtab_shndx_str_id = Some(self.add_section_name(&b".symtab_shndx"[..])); + 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: 4, + 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 + } + + /// 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 or symbols were defined. + /// This must be called after [`Self::add_dynamic_string`]. + pub fn reserve_dynstr(&mut self) { + debug_assert_eq!(self.dynstr_offset, 0); + if !self.need_dynstr { + return; + } + // 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); + } + + /// 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. + /// + /// This must be called before [`Self::reserve_section_headers`]. + pub fn reserve_dynstr_section_index(&mut self) -> SectionIndex { + debug_assert_eq!(self.dynstr_index, SectionIndex(0)); + self.dynstr_str_id = Some(self.add_section_name(&b".dynstr"[..])); + 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; + // The symtab must link to a strtab. + self.need_dynstr = true; + 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; + // The symtab must link to a strtab. + self.need_dynstr = true; + } + 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) { + debug_assert_eq!(self.dynsym_offset, 0); + if self.dynsym_num == 0 { + return; + } + self.dynsym_offset = self.reserve( + self.dynsym_num as usize * self.symbol_size(), + self.elf_align, + ); + } + + /// 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::<Endianness>::default()); + } else { + self.buffer.write(&elf::Sym32::<Endianness>::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 { + debug_assert_eq!(self.dynsym_index, SectionIndex(0)); + self.dynsym_str_id = Some(self.add_section_name(&b".dynsym"[..])); + 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.symbol_size() as u64, + sh_link: self.dynstr_index.0, + sh_info: num_local, + sh_addralign: self.elf_align as u64, + sh_entsize: self.symbol_size() as u64, + }); + } + + fn dyn_size(&self) -> usize { + if self.is_64 { + mem::size_of::<elf::Dyn64<Endianness>>() + } else { + mem::size_of::<elf::Dyn32<Endianness>>() + } + } + + /// 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) { + debug_assert_eq!(self.dynamic_offset, 0); + if dynamic_num == 0 { + return; + } + self.dynamic_num = dynamic_num; + self.dynamic_offset = self.reserve(dynamic_num * self.dyn_size(), self.elf_align); + } + + /// 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()); + } + + /// 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) { + debug_assert!(self.dynamic_offset <= self.buffer.len()); + 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); + } + debug_assert!( + self.dynamic_offset + self.dynamic_num * self.dyn_size() >= self.buffer.len() + ); + } + + /// 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.dyn_size()) as u64, + sh_link: self.dynstr_index.0, + sh_info: 0, + sh_addralign: self.elf_align as u64, + sh_entsize: self.dyn_size() as u64, + }); + } + + fn rel_size(&self, is_rela: bool) -> usize { + if self.is_64 { + if is_rela { + mem::size_of::<elf::Rela64<Endianness>>() + } else { + mem::size_of::<elf::Rel64<Endianness>>() + } + } else { + if is_rela { + mem::size_of::<elf::Rela32<Endianness>>() + } else { + mem::size_of::<elf::Rel32<Endianness>>() + } + } + } + + /// 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) { + self.hash_size = mem::size_of::<elf::HashHeader<Endianness>>() + + bucket_count as usize * 4 + + chain_count as usize * 4; + self.hash_offset = self.reserve(self.hash_size, self.elf_align); + } + + /// 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<F>(&mut self, bucket_count: u32, chain_count: u32, hash: F) + where + F: Fn(u32) -> Option<u32>, + { + 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, self.elf_align); + 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 { + debug_assert!(self.hash_str_id.is_none()); + self.hash_str_id = Some(self.add_section_name(&b".hash"[..])); + 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: self.elf_align 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) { + self.gnu_hash_size = mem::size_of::<elf::GnuHashHeader<Endianness>>() + + bloom_count as usize * self.elf_align + + bucket_count as usize * 4 + + symbol_count as usize * 4; + self.gnu_hash_offset = self.reserve(self.gnu_hash_size, self.elf_align); + } + + /// 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<F>( + &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 { + debug_assert!(self.gnu_hash_str_id.is_none()); + self.gnu_hash_str_id = Some(self.add_section_name(&b".gnu.hash"[..])); + 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: 0, + }); + } + + /// 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) { + debug_assert_eq!(self.gnu_versym_offset, 0); + if self.dynsym_num == 0 { + return; + } + self.gnu_versym_offset = self.reserve(self.dynsym_num as usize * 2, 2); + } + + /// 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, 2); + 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 { + debug_assert!(self.gnu_versym_str_id.is_none()); + self.gnu_versym_str_id = Some(self.add_section_name(&b".gnu.version"[..])); + 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.dynsym_num as u64 * 2, + sh_link: self.dynsym_index.0, + sh_info: 0, + sh_addralign: 2, + 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) { + debug_assert_eq!(self.gnu_verdef_offset, 0); + if verdef_count == 0 { + return; + } + self.gnu_verdef_size = verdef_count * mem::size_of::<elf::Verdef<Endianness>>() + + verdaux_count * mem::size_of::<elf::Verdaux<Endianness>>(); + self.gnu_verdef_offset = self.reserve(self.gnu_verdef_size, self.elf_align); + self.gnu_verdef_count = verdef_count as u16; + self.gnu_verdef_remaining = self.gnu_verdef_count; + } + + /// 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, self.elf_align); + 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::<elf::Verdef<Endianness>>() as u32 + + verdef.aux_count as u32 * mem::size_of::<elf::Verdaux<Endianness>>() as u32 + }; + + self.gnu_verdaux_remaining = verdef.aux_count; + let vd_aux = if verdef.aux_count == 0 { + 0 + } else { + mem::size_of::<elf::Verdef<Endianness>>() 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 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::<elf::Verdaux<Endianness>>() 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 { + debug_assert!(self.gnu_verdef_str_id.is_none()); + self.gnu_verdef_str_id = Some(self.add_section_name(&b".gnu.version_d"[..])); + 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: self.elf_align 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) { + debug_assert_eq!(self.gnu_verneed_offset, 0); + if verneed_count == 0 { + return; + } + self.gnu_verneed_size = verneed_count * mem::size_of::<elf::Verneed<Endianness>>() + + vernaux_count * mem::size_of::<elf::Vernaux<Endianness>>(); + self.gnu_verneed_offset = self.reserve(self.gnu_verneed_size, self.elf_align); + self.gnu_verneed_count = verneed_count as u16; + self.gnu_verneed_remaining = self.gnu_verneed_count; + } + + /// 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, self.elf_align); + 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::<elf::Verneed<Endianness>>() as u32 + + verneed.aux_count as u32 * mem::size_of::<elf::Vernaux<Endianness>>() as u32 + }; + + self.gnu_vernaux_remaining = verneed.aux_count; + let vn_aux = if verneed.aux_count == 0 { + 0 + } else { + mem::size_of::<elf::Verneed<Endianness>>() 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::<elf::Vernaux<Endianness>>() 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 { + debug_assert!(self.gnu_verneed_str_id.is_none()); + self.gnu_verneed_str_id = Some(self.add_section_name(&b".gnu.version_r"[..])); + 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: self.elf_align as u64, + sh_entsize: 0, + }); + } + + /// Reserve the section index for the `.gnu.attributes` section. + pub fn reserve_gnu_attributes_section_index(&mut self) -> SectionIndex { + debug_assert!(self.gnu_attributes_str_id.is_none()); + self.gnu_attributes_str_id = Some(self.add_section_name(&b".gnu.attributes"[..])); + self.reserve_section_index() + } + + /// Reserve the range for the `.gnu.attributes` section. + pub fn reserve_gnu_attributes(&mut self, gnu_attributes_size: usize) { + debug_assert_eq!(self.gnu_attributes_offset, 0); + if gnu_attributes_size == 0 { + return; + } + self.gnu_attributes_size = gnu_attributes_size; + self.gnu_attributes_offset = self.reserve(self.gnu_attributes_size, self.elf_align); + } + + /// 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.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.rel_size(is_rela)) as u64, + sh_link: symtab.0, + sh_info: section.0, + sh_addralign: self.elf_align as u64, + sh_entsize: self.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<u8>, + 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<u8> { + debug_assert_eq!(self.subsection_offset, 0); + debug_assert_eq!(self.subsubsection_offset, 0); + self.data + } +} + +/// 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<StringId>, + 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<StringId>, + pub section: Option<SectionIndex>, + 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, +} diff --git a/third_party/rust/object/src/write/macho.rs b/third_party/rust/object/src/write/macho.rs new file mode 100644 index 0000000000..e3ce55bb4e --- /dev/null +++ b/third_party/rust/object/src/write/macho.rs @@ -0,0 +1,966 @@ +use core::mem; + +use crate::endian::*; +use crate::macho; +use crate::write::string::*; +use crate::write::util::*; +use crate::write::*; +use crate::AddressSize; + +#[derive(Default, Clone, Copy)] +struct SectionOffsets { + index: usize, + offset: usize, + address: u64, + reloc_offset: usize, +} + +#[derive(Default, Clone, Copy)] +struct SymbolOffsets { + emit: bool, + index: usize, + str_id: Option<StringId>, +} + +/// 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::<macho::BuildVersionCommand<Endianness>>(); + debug_assert!(sz <= u32::MAX as usize); + sz as u32 + } +} + +// Public methods. +impl<'a> Object<'a> { + /// 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_set_subsections_via_symbols(&mut self) { + let flags = match self.flags { + FileFlags::MachO { flags } => flags, + _ => 0, + }; + self.flags = FileFlags::MachO { + flags: flags | macho::MH_SUBSECTIONS_VIA_SYMBOLS, + }; + } + + 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, + size: address_size * 8, + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + symbol: tlv_bootstrap, + addend: 0, + }, + ) + .unwrap(); + self.add_relocation( + section, + Relocation { + offset: offset + u64::from(address_size) * 2, + size: address_size * 8, + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + symbol: init_symbol_id, + addend: 0, + }, + ) + .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_fixup_relocation(&mut self, mut relocation: &mut Relocation) -> i64 { + let constant = match relocation.kind { + // AArch64Call relocations have special handling for the addend, so don't adjust it + RelocationKind::Relative if relocation.encoding == RelocationEncoding::AArch64Call => 0, + RelocationKind::Relative + | RelocationKind::GotRelative + | RelocationKind::PltRelative => relocation.addend + 4, + _ => relocation.addend, + }; + // Aarch64 relocs of these sizes act as if they are double-word length + if self.architecture == Architecture::Aarch64 && matches!(relocation.size, 12 | 21 | 26) { + relocation.size = 32; + } + relocation.addend -= constant; + constant + } + + 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; + + let build_version_offset = offset; + if let Some(version) = &self.macho_build_version { + offset += version.cmdsize() as usize; + ncmds += 1; + } + + // 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 symtab command. + let symtab_command_offset = offset; + let symtab_command_len = mem::size_of::<macho::SymtabCommand<Endianness>>(); + offset += symtab_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; + } + } + + // Count symbols and add symbol strings to strtab. + let mut strtab = StringTable::default(); + let mut symbol_offsets = vec![SymbolOffsets::default(); self.symbols.len()]; + let mut nsyms = 0; + 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::Null | SymbolKind::Label => { + return Err(Error(format!( + "unimplemented symbol `{}` kind {:?}", + symbol.name().unwrap_or(""), + symbol.kind + ))); + } + } + symbol_offsets[index].emit = true; + symbol_offsets[index].index = nsyms; + nsyms += 1; + if !symbol.name.is_empty() { + symbol_offsets[index].str_id = Some(strtab.add(&symbol.name)); + } + } + + // 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); + offset += strtab_data.len(); + + // Calculate size of relocations. + for (index, section) in self.sections.iter().enumerate() { + let count = section.relocations.len(); + if count != 0 { + offset = align(offset, 4); + section_offsets[index].reloc_offset = offset; + let len = count * mem::size_of::<macho::Relocation<Endianness>>(); + offset += len; + } + } + + // Start writing. + buffer + .reserve(offset) + .map_err(|_| Error(String::from("Cannot allocate buffer")))?; + + // Write file header. + let (cputype, cpusubtype) = match self.architecture { + Architecture::Arm => (macho::CPU_TYPE_ARM, macho::CPU_SUBTYPE_ARM_ALL), + Architecture::Aarch64 => (macho::CPU_TYPE_ARM64, macho::CPU_SUBTYPE_ARM64_ALL), + Architecture::Aarch64_Ilp32 => { + (macho::CPU_TYPE_ARM64_32, macho::CPU_SUBTYPE_ARM64_32_V8) + } + Architecture::I386 => (macho::CPU_TYPE_X86, macho::CPU_SUBTYPE_I386_ALL), + Architecture::X86_64 => (macho::CPU_TYPE_X86_64, macho::CPU_SUBTYPE_X86_64_ALL), + Architecture::PowerPc => (macho::CPU_TYPE_POWERPC, macho::CPU_SUBTYPE_POWERPC_ALL), + Architecture::PowerPc64 => (macho::CPU_TYPE_POWERPC64, macho::CPU_SUBTYPE_POWERPC_ALL), + _ => { + return Err(Error(format!( + "unimplemented architecture {:?}", + self.architecture + ))); + } + }; + + let flags = match self.flags { + FileFlags::MachO { flags } => flags, + _ => 0, + }; + macho.write_mach_header( + buffer, + MachHeader { + cputype, + cpusubtype, + filetype: macho::MH_OBJECT, + ncmds, + sizeofcmds: sizeofcmds as u32, + flags, + }, + ); + + 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 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 => 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.relocations.len() as u32, + flags, + }, + ); + } + + // 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 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 symtab. + write_align(buffer, pointer_align); + debug_assert_eq!(symtab_offset, buffer.len()); + for (index, symbol) in self.symbols.iter().enumerate() { + if !symbol_offsets[index].emit { + continue; + } + // 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); + + // Write relocations. + for (index, section) in self.sections.iter().enumerate() { + if !section.relocations.is_empty() { + write_align(buffer, 4); + debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len()); + for reloc in §ion.relocations { + let r_extern; + let mut 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 r_length = match reloc.size { + 8 => 0, + 16 => 1, + 32 => 2, + 64 => 3, + _ => return Err(Error(format!("unimplemented reloc size {:?}", reloc))), + }; + let (r_pcrel, r_type) = match self.architecture { + Architecture::I386 => match reloc.kind { + RelocationKind::Absolute => (false, macho::GENERIC_RELOC_VANILLA), + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + Architecture::X86_64 => match (reloc.kind, reloc.encoding, reloc.addend) { + (RelocationKind::Absolute, RelocationEncoding::Generic, 0) => { + (false, macho::X86_64_RELOC_UNSIGNED) + } + (RelocationKind::Relative, RelocationEncoding::Generic, -4) => { + (true, macho::X86_64_RELOC_SIGNED) + } + (RelocationKind::Relative, RelocationEncoding::X86RipRelative, -4) => { + (true, macho::X86_64_RELOC_SIGNED) + } + (RelocationKind::Relative, RelocationEncoding::X86Branch, -4) => { + (true, macho::X86_64_RELOC_BRANCH) + } + (RelocationKind::PltRelative, RelocationEncoding::X86Branch, -4) => { + (true, macho::X86_64_RELOC_BRANCH) + } + (RelocationKind::GotRelative, RelocationEncoding::Generic, -4) => { + (true, macho::X86_64_RELOC_GOT) + } + ( + RelocationKind::GotRelative, + RelocationEncoding::X86RipRelativeMovq, + -4, + ) => (true, macho::X86_64_RELOC_GOT_LOAD), + (RelocationKind::MachO { value, relative }, _, _) => (relative, value), + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }, + Architecture::Aarch64 | Architecture::Aarch64_Ilp32 => { + match (reloc.kind, reloc.encoding, reloc.addend) { + (RelocationKind::Absolute, RelocationEncoding::Generic, 0) => { + (false, macho::ARM64_RELOC_UNSIGNED) + } + (RelocationKind::Relative, RelocationEncoding::AArch64Call, 0) => { + (true, macho::ARM64_RELOC_BRANCH26) + } + // Non-zero addend, so we have to encode the addend separately + ( + RelocationKind::Relative, + RelocationEncoding::AArch64Call, + value, + ) => { + // first emit the BR26 relocation + let reloc_info = macho::RelocationInfo { + r_address: reloc.offset as u32, + r_symbolnum, + r_pcrel: true, + r_length, + r_extern: true, + r_type: macho::ARM64_RELOC_BRANCH26, + }; + buffer.write(&reloc_info.relocation(endian)); + + // set up a separate relocation for the addend + r_symbolnum = value as u32; + (false, macho::ARM64_RELOC_ADDEND) + } + ( + RelocationKind::MachO { value, relative }, + RelocationEncoding::Generic, + 0, + ) => (relative, value), + _ => { + return Err(Error(format!( + "unimplemented relocation {:?}", + reloc + ))); + } + } + } + _ => { + if let RelocationKind::MachO { value, relative } = reloc.kind { + (relative, value) + } else { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + } + }; + 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)); + } + } + } + + 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<E> { + endian: E, +} + +impl<E: Endian> MachO for MachO32<E> { + fn mach_header_size(&self) -> usize { + mem::size_of::<macho::MachHeader32<E>>() + } + + fn segment_command_size(&self) -> usize { + mem::size_of::<macho::SegmentCommand32<E>>() + } + + fn section_header_size(&self) -> usize { + mem::size_of::<macho::Section32<E>>() + } + + fn nlist_size(&self) -> usize { + mem::size_of::<macho::Nlist32<E>>() + } + + 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<E> { + endian: E, +} + +impl<E: Endian> MachO for MachO64<E> { + fn mach_header_size(&self) -> usize { + mem::size_of::<macho::MachHeader64<E>>() + } + + fn segment_command_size(&self) -> usize { + mem::size_of::<macho::SegmentCommand64<E>>() + } + + fn section_header_size(&self) -> usize { + mem::size_of::<macho::Section64<E>>() + } + + fn nlist_size(&self) -> usize { + mem::size_of::<macho::Nlist64<E>>() + } + + 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); + } +} diff --git a/third_party/rust/object/src/write/mod.rs b/third_party/rust/object/src/write/mod.rs new file mode 100644 index 0000000000..711ff16d2a --- /dev/null +++ b/third_party/rust/object/src/write/mod.rs @@ -0,0 +1,943 @@ +//! Interface for writing object files. + +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}; +use crate::{ + Architecture, BinaryFormat, ComdatKind, FileFlags, RelocationEncoding, RelocationKind, + SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope, +}; + +#[cfg(feature = "coff")] +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; + +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(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<T> = result::Result<T, Error>; + +/// A writable relocatable object file. +#[derive(Debug)] +pub struct Object<'a> { + format: BinaryFormat, + architecture: Architecture, + endian: Endianness, + sections: Vec<Section<'a>>, + standard_sections: HashMap<StandardSection, SectionId>, + symbols: Vec<Symbol>, + symbol_map: HashMap<Vec<u8>, SymbolId>, + stub_symbols: HashMap<SymbolId, SymbolId>, + comdats: Vec<Comdat>, + /// File flags that are specific to each file format. + pub flags: FileFlags, + /// The symbol name mangling scheme. + pub mangling: Mangling, + /// Mach-O "_tlv_bootstrap" symbol. + tlv_bootstrap: Option<SymbolId>, + #[cfg(feature = "macho")] + macho_build_version: Option<MachOBuildVersion>, +} + +impl<'a> Object<'a> { + /// Create an empty object file. + pub fn new(format: BinaryFormat, architecture: Architecture, endian: Endianness) -> Object<'a> { + Object { + format, + architecture, + endian, + sections: Vec::new(), + standard_sections: HashMap::new(), + symbols: Vec::new(), + symbol_map: HashMap::new(), + stub_symbols: HashMap::new(), + comdats: Vec::new(), + flags: FileFlags::None, + mangling: Mangling::default(format, architecture), + tlv_bootstrap: None, + #[cfg(feature = "macho")] + macho_build_version: None, + } + } + + /// 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 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. + pub fn set_section_data<T>(&mut self, section: SectionId, data: T, align: u64) + where + T: Into<Cow<'a, [u8]>>, + { + self.sections[section.0].set_data(data, align) + } + + /// Append data to an existing section. Returns the section offset of the data. + 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. + 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<u8>, name: Vec<u8>, 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. + pub fn add_subsection( + &mut self, + section: StandardSection, + name: &[u8], + data: &[u8], + align: u64, + ) -> (SectionId, u64) { + let section_id = if self.has_subsections_via_symbols() { + self.set_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 + }; + let offset = self.append_section_data(section_id, data, align); + (section_id, offset) + } + + fn has_subsections_via_symbols(&self) -> bool { + match self.format { + BinaryFormat::Coff | BinaryFormat::Elf | BinaryFormat::Xcoff => false, + BinaryFormat::MachO => true, + _ => unimplemented!(), + } + } + + fn set_subsections_via_symbols(&mut self) { + match self.format { + #[cfg(feature = "macho")] + BinaryFormat::MachO => self.macho_set_subsections_via_symbols(), + _ => unimplemented!(), + } + } + + fn subsection_info( + &self, + section: StandardSection, + value: &[u8], + ) -> (&'static [u8], Vec<u8>, 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<u8> { + 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<SymbolId> { + 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. + 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<u8>) -> 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. + /// + /// Returns the section offset of the data. + pub fn add_symbol_data( + &mut self, + symbol_id: SymbolId, + section: SectionId, + data: &[u8], + align: u64, + ) -> u64 { + 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. + /// + /// Returns the section offset of the data. + pub fn add_symbol_bss( + &mut self, + symbol_id: SymbolId, + section: SectionId, + size: u64, + align: u64, + ) -> u64 { + 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<()> { + let addend = match self.format { + #[cfg(feature = "coff")] + BinaryFormat::Coff => self.coff_fixup_relocation(&mut relocation), + #[cfg(feature = "elf")] + BinaryFormat::Elf => self.elf_fixup_relocation(&mut relocation)?, + #[cfg(feature = "macho")] + BinaryFormat::MachO => self.macho_fixup_relocation(&mut relocation), + #[cfg(feature = "xcoff")] + BinaryFormat::Xcoff => self.xcoff_fixup_relocation(&mut relocation), + _ => unimplemented!(), + }; + if addend != 0 { + self.write_relocation_addend(section, &relocation, addend)?; + } + self.sections[section.0].relocations.push(relocation); + Ok(()) + } + + fn write_relocation_addend( + &mut self, + section: SectionId, + relocation: &Relocation, + addend: i64, + ) -> Result<()> { + let data = self.sections[section.0].data_mut(); + let offset = relocation.offset as usize; + match relocation.size { + 32 => data.write_at(offset, &U32::new(self.endian, addend as u32)), + 64 => data.write_at(offset, &U64::new(self.endian, addend as u64)), + _ => { + return Err(Error(format!( + "unimplemented relocation addend {:?}", + relocation + ))); + } + } + .map_err(|_| { + Error(format!( + "invalid relocation offset {}+{} (max {})", + relocation.offset, + relocation.size, + data.len() + )) + }) + } + + /// Write the object to a `Vec`. + pub fn write(&self) -> Result<Vec<u8>> { + 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<W: io::Write>(&self, w: W) -> result::Result<(), Box<dyn error::Error>> { + 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<u8>, + name: Vec<u8>, + kind: SectionKind, + size: u64, + align: u64, + data: Cow<'a, [u8]>, + relocations: Vec<Relocation>, + symbol: Option<SymbolId>, + /// 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. + pub fn set_data<T>(&mut self, data: T, align: u64) + where + T: Into<Cow<'a, [u8]>>, + { + 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. + 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. + 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<SectionId> { + 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<u8>, + /// 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<SectionId, SymbolId>, +} + +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 size in bits of the place of relocation. + pub size: u8, + /// The operation used to calculate the result of the relocation. + pub kind: RelocationKind, + /// Information about how the result of the relocation operation is encoded in the place. + pub encoding: RelocationEncoding, + /// 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, +} + +/// 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<SectionId>, +} + +/// 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<u8> { + match self { + Mangling::None | Mangling::Elf | Mangling::Coff | Mangling::Xcoff => None, + Mangling::CoffI386 | Mangling::MachO => Some(b'_'), + } + } +} diff --git a/third_party/rust/object/src/write/pe.rs b/third_party/rust/object/src/write/pe.rs new file mode 100644 index 0000000000..70da3a0937 --- /dev/null +++ b/third_party/rust/object/src/write/pe.rs @@ -0,0 +1,847 @@ +//! 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<DataDirectory>, + section_header_num: u16, + sections: Vec<Section>, + + symbol_offset: u32, + symbol_num: u32, + + reloc_blocks: Vec<RelocBlock>, + relocs: Vec<U16<LE>>, + reloc_offset: u32, +} + +impl<'a> Writer<'a> { + /// Create a new `Writer`. + 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::<pe::ImageDosHeader>() 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::<pe::ImageNtHeaders64>() as u32 + } else { + mem::size_of::<pe::ImageNtHeaders32>() as u32 + } + } + + fn optional_header_size(&self) -> u32 { + let size = if self.is_64 { + mem::size_of::<pe::ImageOptionalHeader64>() as u32 + } else { + mem::size_of::<pe::ImageOptionalHeader32>() as u32 + }; + size + self.data_directories.len() as u32 * mem::size_of::<pe::ImageDataDirectory>() 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::<pe::ImageDataDirectory>() 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::<pe::ImageSectionHeader>() 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::<pe::ImageBaseRelocation>() as u32 + self.count * mem::size_of::<u16>() as u32 + } +} diff --git a/third_party/rust/object/src/write/string.rs b/third_party/rust/object/src/write/string.rs new file mode 100644 index 0000000000..b23274a0af --- /dev/null +++ b/third_party/rust/object/src/write/string.rs @@ -0,0 +1,159 @@ +use alloc::vec::Vec; + +#[cfg(feature = "std")] +type IndexSet<K> = indexmap::IndexSet<K>; +#[cfg(not(feature = "std"))] +type IndexSet<K> = indexmap::IndexSet<K, hashbrown::hash_map::DefaultHashBuilder>; + +/// 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<usize>, +} + +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. + 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. + 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). + pub fn write(&mut self, base: usize, w: &mut Vec<u8>) { + 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; + } + } + } +} + +// 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); + } +} diff --git a/third_party/rust/object/src/write/util.rs b/third_party/rust/object/src/write/util.rs new file mode 100644 index 0000000000..b05b14d927 --- /dev/null +++ b/third_party/rust/object/src/write/util.rs @@ -0,0 +1,260 @@ +use 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<T: 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<T: Pod>(&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<T: Pod>(&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<T: Pod>(&mut self, val: &[T]) { + self.write_bytes(bytes_of_slice(val)) + } +} + +impl WritableBuffer for Vec<u8> { + #[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<W> { + writer: W, + len: usize, + result: Result<(), io::Error>, +} + +#[cfg(feature = "std")] +impl<W> StreamingBuffer<W> { + /// 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<W: io::Write> WritableBuffer for StreamingBuffer<W> { + #[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<T: Pod>(self, offset: usize, val: &T) -> Result<(), ()>; +} + +impl<'a> BytesMut for &'a mut [u8] { + #[inline] + fn write_at<T: Pod>(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. +pub(crate) fn write_uleb128(buf: &mut Vec<u8>, 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<W>(buf: &mut Vec<u8>, 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!(vec![].write_at(0, &u32::to_be(0x89ab)), Err(())); + } +} diff --git a/third_party/rust/object/src/write/xcoff.rs b/third_party/rust/object/src/write/xcoff.rs new file mode 100644 index 0000000000..6c9a803845 --- /dev/null +++ b/third_party/rust/object/src/write/xcoff.rs @@ -0,0 +1,556 @@ +use 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, AddressSize}; + +#[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<StringId>, + 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_fixup_relocation(&mut self, mut relocation: &mut Relocation) -> i64 { + let constant = match relocation.kind { + RelocationKind::Relative => relocation.addend + 4, + _ => relocation.addend, + }; + relocation.addend -= constant; + constant + } + + 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::<xcoff::FileHeader64>(), + mem::size_of::<xcoff::SectionHeader64>(), + mem::size_of::<xcoff::Rel64>(), + mem::size_of::<xcoff::Symbol64>(), + ) + } else { + ( + mem::size_of::<xcoff::FileHeader32>(), + mem::size_of::<xcoff::SectionHeader32>(), + mem::size_of::<xcoff::Rel32>(), + mem::size_of::<xcoff::Symbol32>(), + ) + }; + + // 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::Null => xcoff::C_NULL, + 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 => 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 rtype = match reloc.kind { + RelocationKind::Absolute => xcoff::R_POS, + RelocationKind::Relative => xcoff::R_REL, + RelocationKind::Got => xcoff::R_TOC, + RelocationKind::Xcoff(x) => x, + _ => { + return Err(Error(format!("unimplemented relocation {:?}", reloc))); + } + }; + 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), + // Specifies the bit length of the relocatable reference minus one. + r_rsize: (reloc.size - 1), + r_rtype: 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: (reloc.size - 1), + r_rtype: 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(()) + } +} diff --git a/third_party/rust/object/src/xcoff.rs b/third_party/rust/object/src/xcoff.rs new file mode 100644 index 0000000000..0386989261 --- /dev/null +++ b/third_party/rust/object/src/xcoff.rs @@ -0,0 +1,893 @@ +//! 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<BE>, + /// Number of sections. + pub f_nscns: U16<BE>, + /// Time and date of file creation. + pub f_timdat: U32<BE>, + /// Byte offset to symbol table start. + pub f_symptr: U32<BE>, + /// Number of entries in symbol table. + pub f_nsyms: U32<BE>, + /// Number of bytes in optional header + pub f_opthdr: U16<BE>, + /// Extra flags. + pub f_flags: U16<BE>, +} + +/// 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<BE>, + /// Number of sections. + pub f_nscns: U16<BE>, + /// Time and date of file creation + pub f_timdat: U32<BE>, + /// Byte offset to symbol table start. + pub f_symptr: U64<BE>, + /// Number of bytes in optional header + pub f_opthdr: U16<BE>, + /// Extra flags. + pub f_flags: U16<BE>, + /// Number of entries in symbol table. + pub f_nsyms: U32<BE>, +} + +// 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<BE>, + /// Version. + pub o_vstamp: U16<BE>, + /// Text size in bytes. + pub o_tsize: U32<BE>, + /// Initialized data size in bytes. + pub o_dsize: U32<BE>, + /// Uninitialized data size in bytes. + pub o_bsize: U32<BE>, + /// Entry point descriptor (virtual address). + pub o_entry: U32<BE>, + /// Base address of text (virtual address). + pub o_text_start: U32<BE>, + /// Base address of data (virtual address). + pub o_data_start: U32<BE>, + /// Address of TOC anchor. + pub o_toc: U32<BE>, + /// Section number for entry point. + pub o_snentry: U16<BE>, + /// Section number for .text. + pub o_sntext: U16<BE>, + /// Section number for .data. + pub o_sndata: U16<BE>, + /// Section number for TOC. + pub o_sntoc: U16<BE>, + /// Section number for loader data. + pub o_snloader: U16<BE>, + /// Section number for .bss. + pub o_snbss: U16<BE>, + /// Maximum alignment for .text. + pub o_algntext: U16<BE>, + /// Maximum alignment for .data. + pub o_algndata: U16<BE>, + /// Module type field. + pub o_modtype: U16<BE>, + /// 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<BE>, + /// Maximum data size allowed (bytes). + pub o_maxdata: U32<BE>, + /// Reserved for debuggers. + pub o_debugger: U32<BE>, + /// 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<BE>, + /// Section number for .tbss. + pub o_sntbss: U16<BE>, +} + +/// 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<BE>, + /// Version. + pub o_vstamp: U16<BE>, + /// Reserved for debuggers. + pub o_debugger: U32<BE>, + /// Base address of text (virtual address). + pub o_text_start: U64<BE>, + /// Base address of data (virtual address). + pub o_data_start: U64<BE>, + /// Address of TOC anchor. + pub o_toc: U64<BE>, + /// Section number for entry point. + pub o_snentry: U16<BE>, + /// Section number for .text. + pub o_sntext: U16<BE>, + /// Section number for .data. + pub o_sndata: U16<BE>, + /// Section number for TOC. + pub o_sntoc: U16<BE>, + /// Section number for loader data. + pub o_snloader: U16<BE>, + /// Section number for .bss. + pub o_snbss: U16<BE>, + /// Maximum alignment for .text. + pub o_algntext: U16<BE>, + /// Maximum alignment for .data. + pub o_algndata: U16<BE>, + /// Module type field. + pub o_modtype: U16<BE>, + /// 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<BE>, + /// Initialized data size in bytes. + pub o_dsize: U64<BE>, + /// Uninitialized data size in bytes. + pub o_bsize: U64<BE>, + /// Entry point descriptor (virtual address). + pub o_entry: U64<BE>, + /// Maximum stack size allowed (bytes). + pub o_maxstack: U64<BE>, + /// Maximum data size allowed (bytes). + pub o_maxdata: U64<BE>, + /// Section number for .tdata. + pub o_sntdata: U16<BE>, + /// Section number for .tbss. + pub o_sntbss: U16<BE>, + /// XCOFF64 flags. + pub o_x64flags: U16<BE>, + /// Reserved. + pub o_resv3a: U16<BE>, + /// Reserved. + pub o_resv3: [U32<BE>; 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<BE>, + /// Virtual address (same as physical address). + pub s_vaddr: U32<BE>, + /// Section size. + pub s_size: U32<BE>, + /// Offset in file to raw data for section. + pub s_scnptr: U32<BE>, + /// Offset in file to relocation entries for section. + pub s_relptr: U32<BE>, + /// Offset in file to line number entries for section. + pub s_lnnoptr: U32<BE>, + /// Number of relocation entries. + pub s_nreloc: U16<BE>, + /// Number of line number entries. + pub s_nlnno: U16<BE>, + /// Flags to define the section type. + pub s_flags: U32<BE>, +} + +/// Section header. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct SectionHeader64 { + /// Section name. + pub s_name: [u8; 8], + /// Physical address. + pub s_paddr: U64<BE>, + /// Virtual address (same as physical address). + pub s_vaddr: U64<BE>, + /// Section size. + pub s_size: U64<BE>, + /// Offset in file to raw data for section. + pub s_scnptr: U64<BE>, + /// Offset in file to relocation entries for section. + pub s_relptr: U64<BE>, + /// Offset in file to line number entries for section. + pub s_lnnoptr: U64<BE>, + /// Number of relocation entries. + pub s_nreloc: U32<BE>, + /// Number of line number entries. + pub s_nlnno: U32<BE>, + /// Flags to define the section type. + pub s_flags: U32<BE>, + /// Reserved. + pub s_reserve: U32<BE>, +} + +// 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 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<BE>, + /// Section number of symbol. + pub n_scnum: I16<BE>, + /// Basic and derived type specification. + pub n_type: U16<BE>, + /// 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<BE>, + /// Offset of the name in string table or .debug section. + pub n_offset: U32<BE>, + /// Section number of symbol. + pub n_scnum: I16<BE>, + /// Basic and derived type specification. + pub n_type: U16<BE>, + /// 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; + +// Vlaues 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<BE>, + /// Offset of parameter type-check hash in .typchk section. + pub x_parmhash: U32<BE>, + /// .typchk section number. + pub x_snhash: U16<BE>, + /// Symbol alignment and type. + pub x_smtyp: u8, + /// Storage mapping class. + pub x_smclas: u8, + /// Reserved. + pub x_stab: U32<BE>, + /// x_snstab. + pub x_snstab: U16<BE>, +} + +/// 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<BE>, + /// Offset of parameter type-check hash in .typchk section. + pub x_parmhash: U32<BE>, + /// .typchk section number. + pub x_snhash: U16<BE>, + /// 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<BE>, + /// 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<BE>, + /// Size of function in bytes. + pub x_fsize: U32<BE>, + /// File pointer to line number + pub x_lnnoptr: U32<BE>, + /// Symbol table index of next entry beyond this function. + pub x_endndx: U32<BE>, + /// Pad + pub pad: U16<BE>, +} + +/// Function auxiliary entry. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct FunAux64 { + /// File pointer to line number + pub x_lnnoptr: U64<BE>, + /// Size of function in bytes. + pub x_fsize: U32<BE>, + /// Symbol table index of next entry beyond this function. + pub x_endndx: U32<BE>, + /// 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<BE>, + /// Size of function in bytes. + pub x_fsize: U32<BE>, + /// Symbol table index of next entry beyond this function. + pub x_endndx: U32<BE>, + /// 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<BE>, + /// Low-order 2 bytes of the source line number. + pub x_lnnolo: U16<BE>, + /// 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<BE>, + /// 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<BE>, + /// Number of relocation entries. + pub x_nreloc: U16<BE>, + /// Number of line numbers. + pub x_nlinno: U16<BE>, + /// 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<BE>, + /// Reserved. + pub pad: [u8; 4], + /// Number of relocation entries in section. + pub x_nreloc: U32<BE>, + /// 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<BE>, + /// Number of relocation entries in section. + pub x_nreloc: U64<BE>, + /// 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<BE>, + /// Symbol table index of item that is referenced. + pub r_symndx: U32<BE>, + /// 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<BE>, + /// Symbol table index of item that is referenced. + pub r_symndx: U32<BE>, + /// 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, +); diff --git a/third_party/rust/object/tests/integration.rs b/third_party/rust/object/tests/integration.rs new file mode 100644 index 0000000000..6ebcb547f8 --- /dev/null +++ b/third_party/rust/object/tests/integration.rs @@ -0,0 +1,2 @@ +mod read; +mod round_trip; diff --git a/third_party/rust/object/tests/parse_self.rs b/third_party/rust/object/tests/parse_self.rs new file mode 100644 index 0000000000..1e7df67158 --- /dev/null +++ b/third_party/rust/object/tests/parse_self.rs @@ -0,0 +1,25 @@ +#![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); +} diff --git a/third_party/rust/object/tests/read/coff.rs b/third_party/rust/object/tests/read/coff.rs new file mode 100644 index 0000000000..dcf3b3c6aa --- /dev/null +++ b/third_party/rust/object/tests/read/coff.rs @@ -0,0 +1,23 @@ +use 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::<Vec<_>>(); + assert_eq!(relocations.len(), 65536); +} diff --git a/third_party/rust/object/tests/read/mod.rs b/third_party/rust/object/tests/read/mod.rs new file mode 100644 index 0000000000..d60d1933b7 --- /dev/null +++ b/third_party/rust/object/tests/read/mod.rs @@ -0,0 +1,3 @@ +#![cfg(feature = "read")] + +mod coff; diff --git a/third_party/rust/object/tests/round_trip/bss.rs b/third_party/rust/object/tests/round_trip/bss.rs new file mode 100644 index 0000000000..1354fcc781 --- /dev/null +++ b/third_party/rust/object/tests/round_trip/bss.rs @@ -0,0 +1,255 @@ +#![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 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 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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + 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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + 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 section = sections.next().unwrap(); + println!("{:?}", section); + assert_eq!(section.name(), Ok("")); + assert_eq!(section.kind(), SectionKind::Metadata); + assert_eq!(section.address(), 0); + assert_eq!(section.size(), 0); + + 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("")); + + 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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + 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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + 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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + 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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + assert_eq!(symbol.address(), 24); + + let symbol = symbols.next(); + assert!(symbol.is_none(), "unexpected symbol {:?}", symbol); +} diff --git a/third_party/rust/object/tests/round_trip/coff.rs b/third_party/rust/object/tests/round_trip/coff.rs new file mode 100644 index 0000000000..6785dc367e --- /dev/null +++ b/third_party/rust/object/tests/round_trip/coff.rs @@ -0,0 +1,56 @@ +use object::read::{Object, ObjectSection}; +use object::{read, write}; +use object::{ + Architecture, BinaryFormat, Endianness, RelocationEncoding, 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, + size: 64, + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + symbol, + addend: 0, + }, + ) + .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); +} diff --git a/third_party/rust/object/tests/round_trip/comdat.rs b/third_party/rust/object/tests/round_trip/comdat.rs new file mode 100644 index 0000000000..7b697a04c0 --- /dev/null +++ b/third_party/rust/object/tests/round_trip/comdat.rs @@ -0,0 +1,225 @@ +#![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, offset) = + object.add_subsection(write::StandardSection::Text, b"s1", &[0, 1, 2, 3], 4); + object.section_symbol(section1); + let (section2, _) = + object.add_subsection(write::StandardSection::Data, b"s1", &[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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + 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, offset) = + object.add_subsection(write::StandardSection::Text, b"s1", &[0, 1, 2, 3], 4); + let (section2, _) = + object.add_subsection(write::StandardSection::Data, b"s1", &[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("")); + + 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(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("")); + + 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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + 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); +} diff --git a/third_party/rust/object/tests/round_trip/common.rs b/third_party/rust/object/tests/round_trip/common.rs new file mode 100644 index 0000000000..74d443830f --- /dev/null +++ b/third_party/rust/object/tests/round_trip/common.rs @@ -0,0 +1,245 @@ +#![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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + 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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + 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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), true); + 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("")); + + 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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + 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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + 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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + 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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + assert_eq!(symbol.address(), 8); + + let symbol = symbols.next(); + assert!(symbol.is_none(), "unexpected symbol {:?}", symbol); +} diff --git a/third_party/rust/object/tests/round_trip/elf.rs b/third_party/rust/object/tests/round_trip/elf.rs new file mode 100644 index 0000000000..0aafadc865 --- /dev/null +++ b/third_party/rust/object/tests/round_trip/elf.rs @@ -0,0 +1,289 @@ +use 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, offset) = + object.add_subsection(write::StandardSection::Text, &name, &[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().skip(1) { + assert_eq!( + symbol.section(), + SymbolSection::Section(SectionIndex(symbol.index().0)) + ); + } +} + +#[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 _ = sections.next().unwrap(); + + 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::<LE>::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(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(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(b"name1\0\0\0").unwrap(); + buffer.write(b"descriptor\0\0").unwrap(); + + buffer + .write(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(b"name2\0\0\0").unwrap(); + buffer.write(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(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(b"name1\0\0\0\0\0\0\0").unwrap(); + buffer.write(b"descriptor\0\0\0\0\0\0").unwrap(); + + buffer + .write(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(b"abc\0").unwrap(); + buffer.write(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::<elf::FileHeader32<Endianness>>(Architecture::I386); + gnu_property_inner::<elf::FileHeader64<Endianness>>(Architecture::X86_64); +} + +fn gnu_property_inner<Elf: FileHeader<Endian = Endianness>>(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()); +} diff --git a/third_party/rust/object/tests/round_trip/macho.rs b/third_party/rust/object/tests/round_trip/macho.rs new file mode 100644 index 0000000000..f45d3db126 --- /dev/null +++ b/third_party/rust/object/tests/round_trip/macho.rs @@ -0,0 +1,63 @@ +use 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, + ); + + // Odd number of sections ensures that the starting file offset is not a multiple of 32. + // 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, &vec![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, &vec![0u8; 1], 32); + + let section = object.add_section(vec![], vec![], object::SectionKind::Text); + object.append_section_data(section, &vec![0u8; 1], 1); + + let bytes = &*object.write().unwrap(); + let object = read::File::parse(bytes).unwrap(); + let mut sections = object.sections(); + + let section = sections.next().unwrap(); + assert_eq!(section.file_range(), Some((368, 32))); + assert_eq!(section.address(), 0); + assert_eq!(section.size(), 32); + + let section = sections.next().unwrap(); + assert_eq!(section.file_range(), Some((400, 1))); + assert_eq!(section.address(), 32); + assert_eq!(section.size(), 1); +} diff --git a/third_party/rust/object/tests/round_trip/mod.rs b/third_party/rust/object/tests/round_trip/mod.rs new file mode 100644 index 0000000000..cd696f608a --- /dev/null +++ b/third_party/rust/object/tests/round_trip/mod.rs @@ -0,0 +1,637 @@ +#![cfg(all(feature = "read", feature = "write"))] + +use object::read::{Object, ObjectSection, ObjectSymbol}; +use object::{read, write, SectionIndex}; +use object::{ + Architecture, BinaryFormat, Endianness, RelocationEncoding, 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_x86_64() { + let mut object = + write::Object::new(BinaryFormat::Coff, 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, + size: 64, + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + symbol: func1_symbol, + addend: 0, + }, + ) + .unwrap(); + + 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); + 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_eq!(symbol.is_weak(), false); + + 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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + + 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_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, + size: 64, + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + symbol: func1_symbol, + addend: 0, + }, + ) + .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 section = sections.next().unwrap(); + println!("{:?}", section); + assert_eq!(section.name(), Ok("")); + assert_eq!(section.kind(), SectionKind::Metadata); + assert_eq!(section.address(), 0); + assert_eq!(section.size(), 0); + + 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("")); + assert_eq!(symbol.address(), 0); + assert_eq!(symbol.kind(), SymbolKind::Null); + assert_eq!(symbol.section_index(), None); + assert_eq!(symbol.scope(), SymbolScope::Unknown); + assert_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), true); + + 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_eq!(symbol.is_weak(), false); + + 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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + + 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::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::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, + size: 32, + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + symbol, + addend: 0, + }, + ) + .unwrap(); + if arch.address_size().unwrap().bytes() >= 8 { + object + .add_relocation( + section, + write::Relocation { + offset: 16, + size: 64, + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + symbol, + addend: 0, + }, + ) + .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 section = sections.next().unwrap(); + println!("{:?}", section); + assert_eq!(section.name(), Ok("")); + assert_eq!(section.kind(), SectionKind::Metadata); + assert_eq!(section.address(), 0); + assert_eq!(section.size(), 0); + + 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, + size: 64, + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + symbol: func1_symbol, + addend: 0, + }, + ) + .unwrap(); + object + .add_relocation( + text, + write::Relocation { + offset: 16, + size: 32, + kind: RelocationKind::Relative, + encoding: RelocationEncoding::Generic, + symbol: func1_symbol, + addend: -4, + }, + ) + .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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + + 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 (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 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, endian) in [ + (Architecture::Aarch64, Endianness::Little), + (Architecture::Aarch64_Ilp32, Endianness::Little), + /* TODO: + (Architecture::Arm, Endianness::Little), + */ + (Architecture::I386, Endianness::Little), + (Architecture::X86_64, Endianness::Little), + /* TODO: + (Architecture::PowerPc, Endianness::Big), + (Architecture::PowerPc64, Endianness::Big), + */ + ] + .iter() + .copied() + { + let mut object = write::Object::new(BinaryFormat::MachO, 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, + size: 32, + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + symbol, + addend: 0, + }, + ) + .unwrap(); + if arch.address_size().unwrap().bytes() >= 8 { + object + .add_relocation( + section, + write::Relocation { + offset: 16, + size: 64, + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + symbol, + addend: 0, + }, + ) + .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.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(); + + 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); + } + } +} + +#[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, + size: 64, + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + symbol: func1_symbol, + addend: 0, + }, + ) + .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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + + 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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + + 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); + } +} diff --git a/third_party/rust/object/tests/round_trip/section_flags.rs b/third_party/rust/object/tests/round_trip/section_flags.rs new file mode 100644 index 0000000000..b1ca398b4c --- /dev/null +++ b/third_party/rust/object/tests/round_trip/section_flags.rs @@ -0,0 +1,90 @@ +#![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(); + sections.next().unwrap(); + 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, + } + ); +} diff --git a/third_party/rust/object/tests/round_trip/tls.rs b/third_party/rust/object/tests/round_trip/tls.rs new file mode 100644 index 0000000000..999e2f1815 --- /dev/null +++ b/third_party/rust/object/tests/round_trip/tls.rs @@ -0,0 +1,316 @@ +#![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!(§ion.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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); +} + +#[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); + assert_eq!(section.name(), Ok("")); + + 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!(§ion.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!(§ion.data().unwrap()[..], &[]); + + let mut symbols = object.symbols(); + + let symbol = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Ok("")); + + 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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + 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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + 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); + 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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + + 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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + + 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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), true); + + 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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + + 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_eq!(symbol.is_weak(), false); + assert_eq!(symbol.is_undefined(), false); + + let mut relocations = thread_vars.relocations(); + + 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); + + 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, 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, 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); +} |