diff options
Diffstat (limited to 'third_party/rust/object')
27 files changed, 7681 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..192cc437c1 --- /dev/null +++ b/third_party/rust/object/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.lock":"23339a38e8eddf9001484c2ff7664863025e0eb6c642aba87a5b06c4446c2b81","Cargo.toml":"8e68d4cad051cfed2b1ceb6d208bda6f895ef20ed3244b75aff382f4581151d3","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"0b74dfa0bcee5c420c6b7f67b4b2658f9ab8388c97b8e733975f2cecbdd668a6","README.md":"5c498b93ff10c038784c5b62c346858c0d53d9d6d1591841c5b6724b0abc9415","examples/nm.rs":"9885cb85700512d63e537b4b60bd2c840aa076721eae2059ba6ae3651be1282e","examples/objcopy.rs":"6b51b89bea2d9bd5d8651942a2a5c5637e4553b8beeda47221470f676f23ede0","examples/objdump.rs":"07a23a2f74b7e46d4cdcf4dab23357a39410b4c4db43179c17e059011e40d45c","src/common.rs":"835207072807c7b7a84c6d52b285cb257480428940def96c951a0ca219db615f","src/lib.rs":"7e559b0af48faca419086a743e3f99794e10a91e8619f8c6e26f113d1935fe14","src/read/any.rs":"f43c2f2285488eb8d7d4b85b393a1c9bb6afdefaafd08dba5b09065fbf079f99","src/read/coff.rs":"f1208d189dfa1b728a6406eea64af8cef19389b70ab1fbd91e94b08bf8cf3d5e","src/read/elf.rs":"93807a74f3122be99cecebe4810d686c27be10df710dbc9751ed2c8d4f4a2acd","src/read/macho.rs":"ca308b21073b9696dd1176b201a0a262eb1f76dd87bc46aee95e30585d06ebae","src/read/mod.rs":"efdb99a566a971bca815e1d1dd85b9e9800fbe4e3572cf54a7b0ff54111469c2","src/read/pe.rs":"cfe0ad92b3ada363519f4cdc8f4cd8fdb2cee25809867ace9e54113e3a4bd36b","src/read/traits.rs":"a091b9e71a464f596d1c8aa3d4b57d1cdd44793a22cfe0c1cae9f2d07e326026","src/read/wasm.rs":"fd693e2f7fe56c23469c40305a1662e0627f35ee2777d470c3e2c670addf159a","src/write/coff.rs":"906593d3b1a6b609c0293edea66c289d2da1a35f7cce79194bac788054d601db","src/write/elf.rs":"ee50be8c85e12d1c42392564b2b44a1b9a78444b3eaf27965ea08c28fef91e5f","src/write/macho.rs":"d26bc671da61997d3b09d1d7895a4e518e45d0dcb85e948341b1c191eb3cb6bd","src/write/mod.rs":"91dfdea43bbfea5617047c9a3af42910de8298e06e472216c3d182ec4fec9b02","src/write/string.rs":"a0640f9e0626ca4618a2ac88e17e0d14b053f880d60635ea932ca78b50a114f5","src/write/util.rs":"9629903d556036aa7d6031cffce1fd38c033453a28c0a30eb34fc77aded4a11d","tests/round_trip.rs":"b101e86d9be1191a64495511fb0709d85d813ca140d3fd0884e72129b8100ea2","tests/tls.rs":"916d701b88a11237829de73a56f22b0b9b75ed4688f92348c32629928f1eaca3"},"package":"a3c61759aa254402e53c79a68dc519cda1ceee2ff2b6d70b3e58bf64ac2f03e3"}
\ No newline at end of file diff --git a/third_party/rust/object/Cargo.lock b/third_party/rust/object/Cargo.lock new file mode 100644 index 0000000000..7a8546e5a9 --- /dev/null +++ b/third_party/rust/object/Cargo.lock @@ -0,0 +1,246 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "adler32" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "build_const" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cc" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "crc" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crc32fast" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "flate2" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz-sys 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide_c_api 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "goblin" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "scroll 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "indexmap" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.51" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "log" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miniz-sys" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miniz_oxide" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miniz_oxide_c_api" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "object" +version = "0.16.0" +dependencies = [ + "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "goblin 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", + "scroll 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parity-wasm" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro2" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scroll" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "scroll_derive 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scroll_derive" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "target-lexicon" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "uuid" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" +"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" +"checksum cc 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)" = "5e5f3fee5eeb60324c2781f1e41286bdee933850fff9b3c672587fed5ec58c83" +"checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4" +"checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" +"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" +"checksum flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f87e68aa82b2de08a6e037f1385455759df6e445a8df5e005b4297191dbf18aa" +"checksum goblin 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e6040506480da04a63de51a478e8021892d65d8411f29b2a422c2648bdd8bcb" +"checksum indexmap 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a4d6d89e0948bf10c08b9ecc8ac5b83f07f857ebe2c0cbe38de15b4e4f510356" +"checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917" +"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" +"checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +"checksum miniz-sys 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0300eafb20369952951699b68243ab4334f4b10a88f411c221d444b36c40e649" +"checksum miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c468f2369f07d651a5d0bb2c9079f8488a66d5466efe42d0c5c6466edcb7f71e" +"checksum miniz_oxide_c_api 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7fe927a42e3807ef71defb191dc87d4e24479b221e67015fe38ae2b7b447bab" +"checksum parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865" +"checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" +"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum scroll 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "abb2332cb595d33f7edd5700f4cbf94892e680c7f0ae56adab58a35190b66cb1" +"checksum scroll_derive 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8584eea9b9ff42825b46faf46a8c24d2cff13ec152fa2a50df788b87c07ee28" +"checksum syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "661641ea2aa15845cddeb97dad000d22070bb5c1fb456b96c1cba883ec691e92" +"checksum target-lexicon 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6f4c118a7a38378f305a9e111fcb2f7f838c0be324bfb31a77ea04f7f6e684b4" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" +"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/third_party/rust/object/Cargo.toml b/third_party/rust/object/Cargo.toml new file mode 100644 index 0000000000..c8688f079b --- /dev/null +++ b/third_party/rust/object/Cargo.toml @@ -0,0 +1,69 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +edition = "2018" +name = "object" +version = "0.16.0" +authors = ["Nick Fitzgerald <fitzgen@gmail.com>", "Philip Craig <philipjcraig@gmail.com>"] +exclude = ["/.coveralls.yml", "/.travis.yml"] +description = "A unified interface for parsing object file formats." +keywords = ["object", "loader", "elf", "mach-o", "pe"] +license = "Apache-2.0/MIT" +repository = "https://github.com/gimli-rs/object" +[package.metadata.docs.rs] +all-features = true + +[[example]] +name = "objcopy" +required-features = ["read", "write"] +[dependencies.crc32fast] +version = "1.2" +optional = true + +[dependencies.flate2] +version = "1" +optional = true + +[dependencies.goblin] +version = "0.1" +features = ["endian_fd", "elf32", "elf64", "mach32", "mach64", "pe32", "pe64", "archive"] +default-features = false + +[dependencies.indexmap] +version = "1.1" +optional = true + +[dependencies.parity-wasm] +version = "0.41.0" +optional = true + +[dependencies.scroll] +version = "0.10" +default-features = false + +[dependencies.target-lexicon] +version = "0.9" + +[dependencies.uuid] +version = "0.8" +default-features = false +[dev-dependencies.memmap] +version = "0.7" + +[features] +compression = ["flate2"] +default = ["read", "std", "compression", "wasm"] +read = [] +std = ["goblin/std"] +wasm = ["std", "parity-wasm"] +write = ["crc32fast", "indexmap", "std"] 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..b0586d44cd --- /dev/null +++ b/third_party/rust/object/README.md @@ -0,0 +1,3 @@ +# `object` + +A library for parsing object files across platforms with a single interface. diff --git a/third_party/rust/object/examples/nm.rs b/third_party/rust/object/examples/nm.rs new file mode 100644 index 0000000000..eeece1718b --- /dev/null +++ b/third_party/rust/object/examples/nm.rs @@ -0,0 +1,92 @@ +use object::{Object, ObjectSection, SectionIndex, SectionKind, Symbol, SymbolKind}; +use std::collections::HashMap; +use std::{env, fs, process}; + +fn main() { + let arg_len = env::args().len(); + if arg_len <= 1 { + eprintln!("Usage: {} <file> ...", env::args().next().unwrap()); + process::exit(1); + } + + for file_path in env::args().skip(1) { + if arg_len > 2 { + println!(); + println!("{}:", file_path); + } + + let file = match fs::File::open(&file_path) { + Ok(file) => file, + Err(err) => { + println!("Failed to open file '{}': {}", file_path, err,); + continue; + } + }; + let file = match unsafe { memmap::Mmap::map(&file) } { + Ok(mmap) => mmap, + Err(err) => { + println!("Failed to map file '{}': {}", file_path, err,); + continue; + } + }; + let file = match object::File::parse(&*file) { + Ok(file) => file, + Err(err) => { + println!("Failed to parse file '{}': {}", file_path, err); + continue; + } + }; + + let section_kinds = file.sections().map(|s| (s.index(), s.kind())).collect(); + + println!("Debugging symbols:"); + for (_, symbol) in file.symbols() { + print_symbol(&symbol, §ion_kinds); + } + println!(); + + println!("Dynamic symbols:"); + for (_, symbol) in file.dynamic_symbols() { + print_symbol(&symbol, §ion_kinds); + } + } +} + +fn print_symbol(symbol: &Symbol<'_>, section_kinds: &HashMap<SectionIndex, SectionKind>) { + if let SymbolKind::Section | SymbolKind::File = symbol.kind() { + return; + } + + let mut kind = match symbol + .section_index() + .and_then(|index| section_kinds.get(&index)) + { + Some(SectionKind::Unknown) + | Some(SectionKind::Other) + | Some(SectionKind::OtherString) + | Some(SectionKind::Debug) + | Some(SectionKind::Linker) + | Some(SectionKind::Metadata) => '?', + Some(SectionKind::Text) => 't', + Some(SectionKind::Data) | Some(SectionKind::Tls) | Some(SectionKind::TlsVariables) => 'd', + Some(SectionKind::ReadOnlyData) | Some(SectionKind::ReadOnlyString) => 'r', + Some(SectionKind::UninitializedData) | Some(SectionKind::UninitializedTls) => 'b', + None => 'U', + }; + + if symbol.is_global() { + kind = kind.to_ascii_uppercase(); + } + + if symbol.is_undefined() { + print!("{:16} ", ""); + } else { + print!("{:016x} ", symbol.address()); + } + println!( + "{:016x} {} {}", + symbol.size(), + kind, + symbol.name().unwrap_or("<unknown>"), + ); +} diff --git a/third_party/rust/object/examples/objcopy.rs b/third_party/rust/object/examples/objcopy.rs new file mode 100644 index 0000000000..c3dc44f2e5 --- /dev/null +++ b/third_party/rust/object/examples/objcopy.rs @@ -0,0 +1,117 @@ +use std::collections::HashMap; +use std::{env, fs, process}; + +use object::{write, Object, ObjectSection, RelocationTarget, SectionKind, SymbolKind}; + +fn main() { + let mut args = env::args(); + if args.len() != 3 { + eprintln!("Usage: {} <infile> <outfile>", args.next().unwrap()); + process::exit(1); + } + + args.next(); + let in_file_path = args.next().unwrap(); + let out_file_path = args.next().unwrap(); + + let in_file = match fs::File::open(&in_file_path) { + Ok(file) => file, + Err(err) => { + eprintln!("Failed to open file '{}': {}", in_file_path, err,); + process::exit(1); + } + }; + let in_file = match unsafe { memmap::Mmap::map(&in_file) } { + Ok(mmap) => mmap, + Err(err) => { + eprintln!("Failed to map file '{}': {}", in_file_path, err,); + process::exit(1); + } + }; + let in_object = match object::File::parse(&*in_file) { + Ok(object) => object, + Err(err) => { + eprintln!("Failed to parse file '{}': {}", in_file_path, err); + process::exit(1); + } + }; + + let mut out_object = write::Object::new(in_object.format(), in_object.architecture()); + out_object.mangling = write::Mangling::None; + + let mut out_sections = HashMap::new(); + for in_section in in_object.sections() { + if in_section.kind() == SectionKind::Metadata { + continue; + } + let section_id = out_object.add_section( + in_section.segment_name().unwrap_or("").as_bytes().to_vec(), + in_section.name().unwrap_or("").as_bytes().to_vec(), + in_section.kind(), + ); + let out_section = out_object.section_mut(section_id); + if out_section.is_bss() { + out_section.append_bss(in_section.size(), in_section.align()); + } else { + out_section.set_data(in_section.uncompressed_data().into(), in_section.align()); + } + out_sections.insert(in_section.index(), section_id); + } + + let mut out_symbols = HashMap::new(); + for (symbol_index, in_symbol) in in_object.symbols() { + if in_symbol.kind() == SymbolKind::Null { + continue; + } + let (section, value) = match in_symbol.section_index() { + Some(index) => ( + Some(*out_sections.get(&index).unwrap()), + in_symbol.address() - in_object.section_by_index(index).unwrap().address(), + ), + None => (None, in_symbol.address()), + }; + let out_symbol = write::Symbol { + name: in_symbol.name().unwrap_or("").as_bytes().to_vec(), + value, + size: in_symbol.size(), + kind: in_symbol.kind(), + scope: in_symbol.scope(), + weak: in_symbol.is_weak(), + section, + }; + let symbol_id = out_object.add_symbol(out_symbol); + out_symbols.insert(symbol_index, symbol_id); + } + + for in_section in in_object.sections() { + if in_section.kind() == SectionKind::Metadata { + continue; + } + let out_section = *out_sections.get(&in_section.index()).unwrap(); + for (offset, in_relocation) in in_section.relocations() { + let symbol = match in_relocation.target() { + RelocationTarget::Symbol(symbol) => *out_symbols.get(&symbol).unwrap(), + RelocationTarget::Section(section) => { + out_object.section_symbol(*out_sections.get(§ion).unwrap()) + } + }; + let out_relocation = write::Relocation { + offset, + size: in_relocation.size(), + kind: in_relocation.kind(), + encoding: in_relocation.encoding(), + symbol, + addend: in_relocation.addend(), + }; + out_object + .add_relocation(out_section, out_relocation) + .unwrap(); + } + } + + let out_data = out_object.write().unwrap(); + if let Err(err) = fs::write(&out_file_path, out_data) { + eprintln!("Failed to write file '{}': {}", out_file_path, err); + process::exit(1); + } +} diff --git a/third_party/rust/object/examples/objdump.rs b/third_party/rust/object/examples/objdump.rs new file mode 100644 index 0000000000..52b227d9de --- /dev/null +++ b/third_party/rust/object/examples/objdump.rs @@ -0,0 +1,77 @@ +use object::{Object, ObjectSection}; +use std::{env, fs, process}; + +fn main() { + let arg_len = env::args().len(); + if arg_len <= 1 { + eprintln!("Usage: {} <file> ...", env::args().next().unwrap()); + process::exit(1); + } + + for file_path in env::args().skip(1) { + if arg_len > 2 { + println!(); + println!("{}:", file_path); + } + + let file = match fs::File::open(&file_path) { + Ok(file) => file, + Err(err) => { + println!("Failed to open file '{}': {}", file_path, err,); + return; + } + }; + let file = match unsafe { memmap::Mmap::map(&file) } { + Ok(mmap) => mmap, + Err(err) => { + println!("Failed to map file '{}': {}", file_path, err,); + return; + } + }; + let file = match object::File::parse(&*file) { + Ok(file) => file, + Err(err) => { + println!("Failed to parse file '{}': {}", file_path, err); + return; + } + }; + + if let Some(uuid) = file.mach_uuid() { + println!("Mach UUID: {}", uuid); + } + if let Some(build_id) = file.build_id() { + println!("Build ID: {:x?}", build_id); + } + if let Some((filename, crc)) = file.gnu_debuglink() { + println!( + "GNU debug link: {} CRC: {:08x}", + String::from_utf8_lossy(filename), + crc + ); + } + + for segment in file.segments() { + println!("{:?}", segment); + } + + for (index, section) in file.sections().enumerate() { + println!("{}: {:?}", index, section); + } + + for (index, symbol) in file.symbols() { + println!("{}: {:?}", index.0, symbol); + } + + for section in file.sections() { + if section.relocations().next().is_some() { + println!( + "\n{} relocations", + section.name().unwrap_or("<invalid name>") + ); + for relocation in section.relocations() { + println!("{:?}", relocation); + } + } + } + } +} diff --git a/third_party/rust/object/src/common.rs b/third_party/rust/object/src/common.rs new file mode 100644 index 0000000000..6cc3f61488 --- /dev/null +++ b/third_party/rust/object/src/common.rs @@ -0,0 +1,187 @@ +/// The kind of a section. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SectionKind { + /// The section kind is unknown. + Unknown, + /// An executable code section. + /// + /// Example ELF sections: `.text` + /// + /// 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` + ReadOnlyData, + /// 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, + /// 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, + /// Metadata such as symbols or relocations. + /// + /// Example ELF sections: `.symtab`, `.strtab` + Metadata, +} + +/// The kind of a symbol. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SymbolKind { + /// The symbol kind is unknown. + Unknown, + /// The symbol is a null placeholder. + Null, + /// The symbol is for executable code. + Text, + /// The symbol is for a data object. + Data, + /// The symbol is for a section. + Section, + /// The symbol is the name of a file. It precedes symbols within that file. + File, + /// The symbol is for a code label. + Label, + /// The symbol is for an uninitialized common block. + Common, + /// The symbol is for a thread local storage entity. + Tls, +} + +/// A symbol scope. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +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)] +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), +} + +/// 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)] +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, +} diff --git a/third_party/rust/object/src/lib.rs b/third_party/rust/object/src/lib.rs new file mode 100644 index 0000000000..9719575ea8 --- /dev/null +++ b/third_party/rust/object/src/lib.rs @@ -0,0 +1,45 @@ +//! # `object` +//! +//! The `object` crate provides a unified interface to working with object files +//! across platforms. +//! +//! See the [`File` struct](./read/struct.File.html) for details. + +#![deny(missing_docs)] +#![deny(missing_debug_implementations)] +#![no_std] +#![cfg_attr(not(feature = "std"), feature(alloc))] + +#[cfg(feature = "std")] +#[macro_use] +extern crate std; + +#[cfg(all(not(feature = "std"), feature = "compression"))] +#[macro_use] +extern crate alloc; +#[cfg(all(not(feature = "std"), not(feature = "compression")))] +extern crate alloc; +#[cfg(not(feature = "std"))] +extern crate core as std; + +#[cfg(feature = "std")] +mod alloc { + pub use std::borrow; + pub use std::fmt; + pub use std::vec; +} + +// Re-export since these are used in public signatures. +pub use target_lexicon; +pub use uuid; + +mod common; +pub use common::*; + +#[cfg(feature = "read")] +pub mod read; +#[cfg(feature = "read")] +pub use read::*; + +#[cfg(feature = "write")] +pub mod write; 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..74f03f735f --- /dev/null +++ b/third_party/rust/object/src/read/any.rs @@ -0,0 +1,523 @@ +use crate::alloc::borrow::Cow; +use crate::alloc::fmt; +use target_lexicon::{Architecture, BinaryFormat}; +use uuid::Uuid; + +#[cfg(feature = "wasm")] +use crate::read::wasm; +use crate::read::{coff, elf, macho, pe}; +use crate::read::{ + Object, ObjectSection, ObjectSegment, Relocation, SectionIndex, SectionKind, Symbol, + SymbolIndex, SymbolMap, +}; + +/// Evaluate an expression on the contents of a file format enum. +/// +/// This is a hack to avoid virtual calls. +macro_rules! with_inner { + ($inner:expr, $enum:ident, | $var:ident | $body:expr) => { + match $inner { + $enum::Coff(ref $var) => $body, + $enum::Elf(ref $var) => $body, + $enum::MachO(ref $var) => $body, + $enum::Pe(ref $var) => $body, + #[cfg(feature = "wasm")] + $enum::Wasm(ref $var) => $body, + } + }; +} + +macro_rules! with_inner_mut { + ($inner:expr, $enum:ident, | $var:ident | $body:expr) => { + match $inner { + $enum::Coff(ref mut $var) => $body, + $enum::Elf(ref mut $var) => $body, + $enum::MachO(ref mut $var) => $body, + $enum::Pe(ref mut $var) => $body, + #[cfg(feature = "wasm")] + $enum::Wasm(ref mut $var) => $body, + } + }; +} + +/// Like `with_inner!`, but wraps the result in another enum. +macro_rules! map_inner { + ($inner:expr, $from:ident, $to:ident, | $var:ident | $body:expr) => { + match $inner { + $from::Coff(ref $var) => $to::Coff($body), + $from::Elf(ref $var) => $to::Elf($body), + $from::MachO(ref $var) => $to::MachO($body), + $from::Pe(ref $var) => $to::Pe($body), + #[cfg(feature = "wasm")] + $from::Wasm(ref $var) => $to::Wasm($body), + } + }; +} + +/// Like `map_inner!`, but the result is a Result or Option. +macro_rules! map_inner_option { + ($inner:expr, $from:ident, $to:ident, | $var:ident | $body:expr) => { + match $inner { + $from::Coff(ref $var) => $body.map($to::Coff), + $from::Elf(ref $var) => $body.map($to::Elf), + $from::MachO(ref $var) => $body.map($to::MachO), + $from::Pe(ref $var) => $body.map($to::Pe), + #[cfg(feature = "wasm")] + $from::Wasm(ref $var) => $body.map($to::Wasm), + } + }; +} + +/// Call `next` for a file format iterator. +macro_rules! next_inner { + ($inner:expr, $from:ident, $to:ident) => { + match $inner { + $from::Coff(ref mut iter) => iter.next().map($to::Coff), + $from::Elf(ref mut iter) => iter.next().map($to::Elf), + $from::MachO(ref mut iter) => iter.next().map($to::MachO), + $from::Pe(ref mut iter) => iter.next().map($to::Pe), + #[cfg(feature = "wasm")] + $from::Wasm(ref mut iter) => iter.next().map($to::Wasm), + } + }; +} + +/// An object file. +/// +/// Most functionality is provided by the `Object` trait implementation. +#[derive(Debug)] +pub struct File<'data> { + inner: FileInternal<'data>, +} + +#[allow(clippy::large_enum_variant)] +#[derive(Debug)] +enum FileInternal<'data> { + Coff(coff::CoffFile<'data>), + Elf(elf::ElfFile<'data>), + MachO(macho::MachOFile<'data>), + Pe(pe::PeFile<'data>), + #[cfg(feature = "wasm")] + Wasm(wasm::WasmFile), +} + +impl<'data> File<'data> { + /// Parse the raw file data. + pub fn parse(data: &'data [u8]) -> Result<Self, &'static str> { + if data.len() < 16 { + return Err("File too short"); + } + + let inner = match [data[0], data[1], data[2], data[3]] { + // ELF + [0x7f, b'E', b'L', b'F'] => FileInternal::Elf(elf::ElfFile::parse(data)?), + // 32-bit Mach-O + [0xfe, 0xed, 0xfa, 0xce] + | [0xce, 0xfa, 0xed, 0xfe] + // 64-bit Mach-O + | [0xfe, 0xed, 0xfa, 0xcf] + | [0xcf, 0xfa, 0xed, 0xfe] => FileInternal::MachO(macho::MachOFile::parse(data)?), + // WASM + #[cfg(feature = "wasm")] + [0x00, b'a', b's', b'm'] => FileInternal::Wasm(wasm::WasmFile::parse(data)?), + // MS-DOS, assume stub for Windows PE + [b'M', b'Z', _, _] => FileInternal::Pe(pe::PeFile::parse(data)?), + // TODO: more COFF machines + // COFF x86 + [0x4c, 0x01, _, _] + // COFF x86-64 + | [0x64, 0x86, _, _] => FileInternal::Coff(coff::CoffFile::parse(data)?), + _ => return Err("Unknown file magic"), + }; + Ok(File { inner }) + } + + /// Return the file format. + pub fn format(&self) -> BinaryFormat { + match self.inner { + FileInternal::Elf(_) => BinaryFormat::Elf, + FileInternal::MachO(_) => BinaryFormat::Macho, + FileInternal::Coff(_) | FileInternal::Pe(_) => BinaryFormat::Coff, + #[cfg(feature = "wasm")] + FileInternal::Wasm(_) => BinaryFormat::Wasm, + } + } +} + +impl<'data, 'file> Object<'data, 'file> for File<'data> +where + 'data: 'file, +{ + type Segment = Segment<'data, 'file>; + type SegmentIterator = SegmentIterator<'data, 'file>; + type Section = Section<'data, 'file>; + type SectionIterator = SectionIterator<'data, 'file>; + type SymbolIterator = SymbolIterator<'data, 'file>; + + fn architecture(&self) -> Architecture { + with_inner!(self.inner, FileInternal, |x| x.architecture()) + } + + fn is_little_endian(&self) -> bool { + with_inner!(self.inner, FileInternal, |x| x.is_little_endian()) + } + + fn is_64(&self) -> bool { + with_inner!(self.inner, FileInternal, |x| x.is_64()) + } + + fn segments(&'file self) -> SegmentIterator<'data, 'file> { + SegmentIterator { + inner: map_inner!(self.inner, FileInternal, SegmentIteratorInternal, |x| x + .segments()), + } + } + + fn section_by_name(&'file self, section_name: &str) -> Option<Section<'data, 'file>> { + map_inner_option!(self.inner, FileInternal, SectionInternal, |x| x + .section_by_name(section_name)) + .map(|inner| Section { inner }) + } + + fn section_by_index(&'file self, index: SectionIndex) -> Option<Section<'data, 'file>> { + map_inner_option!(self.inner, FileInternal, SectionInternal, |x| x + .section_by_index(index)) + .map(|inner| Section { inner }) + } + + fn section_data_by_name(&self, section_name: &str) -> Option<Cow<'data, [u8]>> { + with_inner!(self.inner, FileInternal, |x| x + .section_data_by_name(section_name)) + } + + fn sections(&'file self) -> SectionIterator<'data, 'file> { + SectionIterator { + inner: map_inner!(self.inner, FileInternal, SectionIteratorInternal, |x| x + .sections()), + } + } + + fn symbol_by_index(&self, index: SymbolIndex) -> Option<Symbol<'data>> { + with_inner!(self.inner, FileInternal, |x| x.symbol_by_index(index)) + } + + fn symbols(&'file self) -> SymbolIterator<'data, 'file> { + SymbolIterator { + inner: map_inner!(self.inner, FileInternal, SymbolIteratorInternal, |x| x + .symbols()), + } + } + + fn dynamic_symbols(&'file self) -> SymbolIterator<'data, 'file> { + SymbolIterator { + inner: map_inner!(self.inner, FileInternal, SymbolIteratorInternal, |x| x + .dynamic_symbols()), + } + } + + fn symbol_map(&self) -> SymbolMap<'data> { + with_inner!(self.inner, FileInternal, |x| x.symbol_map()) + } + + fn has_debug_symbols(&self) -> bool { + with_inner!(self.inner, FileInternal, |x| x.has_debug_symbols()) + } + + #[inline] + fn mach_uuid(&self) -> Option<Uuid> { + with_inner!(self.inner, FileInternal, |x| x.mach_uuid()) + } + + #[inline] + fn build_id(&self) -> Option<&'data [u8]> { + with_inner!(self.inner, FileInternal, |x| x.build_id()) + } + + #[inline] + fn gnu_debuglink(&self) -> Option<(&'data [u8], u32)> { + with_inner!(self.inner, FileInternal, |x| x.gnu_debuglink()) + } + + fn entry(&self) -> u64 { + with_inner!(self.inner, FileInternal, |x| x.entry()) + } +} + +/// An iterator over the segments of a `File`. +#[derive(Debug)] +pub struct SegmentIterator<'data, 'file> +where + 'data: 'file, +{ + inner: SegmentIteratorInternal<'data, 'file>, +} + +#[derive(Debug)] +enum SegmentIteratorInternal<'data, 'file> +where + 'data: 'file, +{ + Coff(coff::CoffSegmentIterator<'data, 'file>), + Elf(elf::ElfSegmentIterator<'data, 'file>), + MachO(macho::MachOSegmentIterator<'data, 'file>), + Pe(pe::PeSegmentIterator<'data, 'file>), + #[cfg(feature = "wasm")] + Wasm(wasm::WasmSegmentIterator<'file>), +} + +impl<'data, 'file> Iterator for SegmentIterator<'data, 'file> { + type Item = Segment<'data, 'file>; + + 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> +where + 'data: 'file, +{ + inner: SegmentInternal<'data, 'file>, +} + +#[derive(Debug)] +enum SegmentInternal<'data, 'file> +where + 'data: 'file, +{ + Coff(coff::CoffSegment<'data, 'file>), + Elf(elf::ElfSegment<'data, 'file>), + MachO(macho::MachOSegment<'data, 'file>), + Pe(pe::PeSegment<'data, 'file>), + #[cfg(feature = "wasm")] + Wasm(wasm::WasmSegment<'file>), +} + +impl<'data, 'file> fmt::Debug for Segment<'data, 'file> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // It's painful to do much better than this + f.debug_struct("Segment") + .field("name", &self.name().unwrap_or("<unnamed>")) + .field("address", &self.address()) + .field("size", &self.data().len()) + .finish() + } +} + +impl<'data, 'file> ObjectSegment<'data> for Segment<'data, 'file> { + fn address(&self) -> u64 { + with_inner!(self.inner, SegmentInternal, |x| x.address()) + } + + fn size(&self) -> u64 { + with_inner!(self.inner, SegmentInternal, |x| x.size()) + } + + fn align(&self) -> u64 { + with_inner!(self.inner, SegmentInternal, |x| x.align()) + } + + fn file_range(&self) -> (u64, u64) { + with_inner!(self.inner, SegmentInternal, |x| x.file_range()) + } + + fn data(&self) -> &'data [u8] { + with_inner!(self.inner, SegmentInternal, |x| x.data()) + } + + fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]> { + with_inner!(self.inner, SegmentInternal, |x| x.data_range(address, size)) + } + + fn name(&self) -> Option<&str> { + with_inner!(self.inner, SegmentInternal, |x| x.name()) + } +} + +/// An iterator of the sections of a `File`. +#[derive(Debug)] +pub struct SectionIterator<'data, 'file> +where + 'data: 'file, +{ + inner: SectionIteratorInternal<'data, 'file>, +} + +// we wrap our enums in a struct so that they are kept private. +#[derive(Debug)] +enum SectionIteratorInternal<'data, 'file> +where + 'data: 'file, +{ + Coff(coff::CoffSectionIterator<'data, 'file>), + Elf(elf::ElfSectionIterator<'data, 'file>), + MachO(macho::MachOSectionIterator<'data, 'file>), + Pe(pe::PeSectionIterator<'data, 'file>), + #[cfg(feature = "wasm")] + Wasm(wasm::WasmSectionIterator<'file>), +} + +impl<'data, 'file> Iterator for SectionIterator<'data, 'file> { + type Item = Section<'data, 'file>; + + 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> +where + 'data: 'file, +{ + inner: SectionInternal<'data, 'file>, +} + +enum SectionInternal<'data, 'file> +where + 'data: 'file, +{ + Coff(coff::CoffSection<'data, 'file>), + Elf(elf::ElfSection<'data, 'file>), + MachO(macho::MachOSection<'data, 'file>), + Pe(pe::PeSection<'data, 'file>), + #[cfg(feature = "wasm")] + Wasm(wasm::WasmSection<'file>), +} + +impl<'data, 'file> fmt::Debug for Section<'data, 'file> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // It's painful to do much better than this + let mut s = f.debug_struct("Section"); + if let Some(segment) = self.segment_name() { + s.field("segment", &segment); + } + s.field("name", &self.name().unwrap_or("<invalid name>")) + .field("address", &self.address()) + .field("size", &self.data().len()) + .field("kind", &self.kind()) + .finish() + } +} + +impl<'data, 'file> ObjectSection<'data> for Section<'data, 'file> { + type RelocationIterator = RelocationIterator<'data, 'file>; + + fn index(&self) -> SectionIndex { + with_inner!(self.inner, SectionInternal, |x| x.index()) + } + + fn address(&self) -> u64 { + with_inner!(self.inner, SectionInternal, |x| x.address()) + } + + fn size(&self) -> u64 { + with_inner!(self.inner, SectionInternal, |x| x.size()) + } + + fn align(&self) -> u64 { + with_inner!(self.inner, SectionInternal, |x| x.align()) + } + + fn file_range(&self) -> Option<(u64, u64)> { + with_inner!(self.inner, SectionInternal, |x| x.file_range()) + } + + fn data(&self) -> Cow<'data, [u8]> { + with_inner!(self.inner, SectionInternal, |x| x.data()) + } + + fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]> { + with_inner!(self.inner, SectionInternal, |x| x.data_range(address, size)) + } + + fn uncompressed_data(&self) -> Cow<'data, [u8]> { + with_inner!(self.inner, SectionInternal, |x| x.uncompressed_data()) + } + + fn name(&self) -> Option<&str> { + with_inner!(self.inner, SectionInternal, |x| x.name()) + } + + fn segment_name(&self) -> Option<&str> { + with_inner!(self.inner, SectionInternal, |x| x.segment_name()) + } + + fn kind(&self) -> SectionKind { + with_inner!(self.inner, SectionInternal, |x| x.kind()) + } + + fn relocations(&self) -> RelocationIterator<'data, 'file> { + RelocationIterator { + inner: map_inner!( + self.inner, + SectionInternal, + RelocationIteratorInternal, + |x| x.relocations() + ), + } + } +} + +/// An iterator over symbol table entries. +#[derive(Debug)] +pub struct SymbolIterator<'data, 'file> +where + 'data: 'file, +{ + inner: SymbolIteratorInternal<'data, 'file>, +} + +#[derive(Debug)] +enum SymbolIteratorInternal<'data, 'file> +where + 'data: 'file, +{ + Coff(coff::CoffSymbolIterator<'data, 'file>), + Elf(elf::ElfSymbolIterator<'data, 'file>), + MachO(macho::MachOSymbolIterator<'data, 'file>), + Pe(pe::PeSymbolIterator<'data, 'file>), + #[cfg(feature = "wasm")] + Wasm(wasm::WasmSymbolIterator<'file>), +} + +impl<'data, 'file> Iterator for SymbolIterator<'data, 'file> { + type Item = (SymbolIndex, Symbol<'data>); + + fn next(&mut self) -> Option<Self::Item> { + with_inner_mut!(self.inner, SymbolIteratorInternal, |x| x.next()) + } +} + +/// An iterator over relocation entries +#[derive(Debug)] +pub struct RelocationIterator<'data, 'file> +where + 'data: 'file, +{ + inner: RelocationIteratorInternal<'data, 'file>, +} + +#[derive(Debug)] +enum RelocationIteratorInternal<'data, 'file> +where + 'data: 'file, +{ + Coff(coff::CoffRelocationIterator<'data, 'file>), + Elf(elf::ElfRelocationIterator<'data, 'file>), + MachO(macho::MachORelocationIterator<'data, 'file>), + Pe(pe::PeRelocationIterator), + #[cfg(feature = "wasm")] + Wasm(wasm::WasmRelocationIterator), +} + +impl<'data, 'file> Iterator for RelocationIterator<'data, 'file> { + type Item = (u64, Relocation); + + fn next(&mut self) -> Option<Self::Item> { + with_inner_mut!(self.inner, RelocationIteratorInternal, |x| x.next()) + } +} diff --git a/third_party/rust/object/src/read/coff.rs b/third_party/rust/object/src/read/coff.rs new file mode 100644 index 0000000000..72f985e458 --- /dev/null +++ b/third_party/rust/object/src/read/coff.rs @@ -0,0 +1,527 @@ +use crate::alloc::borrow::Cow; +use crate::alloc::fmt; +use crate::alloc::vec::Vec; +use goblin::pe; +use std::{iter, slice}; +use target_lexicon::Architecture; + +use crate::read::{ + self, Object, ObjectSection, ObjectSegment, Relocation, RelocationEncoding, RelocationKind, + RelocationTarget, SectionIndex, SectionKind, Symbol, SymbolIndex, SymbolKind, SymbolMap, + SymbolScope, +}; + +/// A COFF object file. +#[derive(Debug)] +pub struct CoffFile<'data> { + coff: pe::Coff<'data>, + data: &'data [u8], +} + +/// An iterator over the loadable sections of a `CoffFile`. +#[derive(Debug)] +pub struct CoffSegmentIterator<'data, 'file> +where + 'data: 'file, +{ + file: &'file CoffFile<'data>, + iter: slice::Iter<'file, pe::section_table::SectionTable>, +} + +/// A loadable section of a `CoffFile`. +#[derive(Debug)] +pub struct CoffSegment<'data, 'file> +where + 'data: 'file, +{ + file: &'file CoffFile<'data>, + section: &'file pe::section_table::SectionTable, +} + +/// An iterator over the sections of a `CoffFile`. +#[derive(Debug)] +pub struct CoffSectionIterator<'data, 'file> +where + 'data: 'file, +{ + file: &'file CoffFile<'data>, + iter: iter::Enumerate<slice::Iter<'file, pe::section_table::SectionTable>>, +} + +/// A section of a `CoffFile`. +#[derive(Debug)] +pub struct CoffSection<'data, 'file> +where + 'data: 'file, +{ + file: &'file CoffFile<'data>, + index: SectionIndex, + section: &'file pe::section_table::SectionTable, +} + +/// An iterator over the symbols of a `CoffFile`. +pub struct CoffSymbolIterator<'data, 'file> +where + 'data: 'file, +{ + file: &'file CoffFile<'data>, + symbols: pe::symbol::SymbolIterator<'data>, +} + +/// An iterator over the relocations in an `CoffSection`. +pub struct CoffRelocationIterator<'data, 'file> { + file: &'file CoffFile<'data>, + relocations: pe::relocation::Relocations<'data>, +} + +impl<'data> CoffFile<'data> { + /// Get the COFF headers of the file. + // TODO: this is temporary to allow access to features this crate doesn't provide yet + #[inline] + pub fn coff(&self) -> &pe::Coff<'data> { + &self.coff + } + + /// Parse the raw COFF file data. + pub fn parse(data: &'data [u8]) -> Result<Self, &'static str> { + let coff = pe::Coff::parse(data).map_err(|_| "Could not parse COFF header")?; + Ok(CoffFile { coff, data }) + } +} + +impl<'data, 'file> Object<'data, 'file> for CoffFile<'data> +where + 'data: 'file, +{ + type Segment = CoffSegment<'data, 'file>; + type SegmentIterator = CoffSegmentIterator<'data, 'file>; + type Section = CoffSection<'data, 'file>; + type SectionIterator = CoffSectionIterator<'data, 'file>; + type SymbolIterator = CoffSymbolIterator<'data, 'file>; + + fn architecture(&self) -> Architecture { + match self.coff.header.machine { + pe::header::COFF_MACHINE_X86 => Architecture::I386, + pe::header::COFF_MACHINE_X86_64 => Architecture::X86_64, + _ => Architecture::Unknown, + } + } + + #[inline] + fn is_little_endian(&self) -> bool { + true + } + + #[inline] + fn is_64(&self) -> bool { + false + } + + fn segments(&'file self) -> CoffSegmentIterator<'data, 'file> { + CoffSegmentIterator { + file: self, + iter: self.coff.sections.iter(), + } + } + + fn section_by_name(&'file self, section_name: &str) -> Option<CoffSection<'data, 'file>> { + self.sections() + .find(|section| section.name() == Some(section_name)) + } + + fn section_by_index(&'file self, index: SectionIndex) -> Option<CoffSection<'data, 'file>> { + self.sections().find(|section| section.index() == index) + } + + fn sections(&'file self) -> CoffSectionIterator<'data, 'file> { + CoffSectionIterator { + file: self, + iter: self.coff.sections.iter().enumerate(), + } + } + + fn symbol_by_index(&self, index: SymbolIndex) -> Option<Symbol<'data>> { + self.coff + .symbols + .get(index.0) + .map(|(name, symbol)| parse_symbol(index.0, name, &symbol, &self.coff)) + } + + fn symbols(&'file self) -> CoffSymbolIterator<'data, 'file> { + CoffSymbolIterator { + file: self, + symbols: self.coff.symbols.iter(), + } + } + + fn dynamic_symbols(&'file self) -> CoffSymbolIterator<'data, 'file> { + CoffSymbolIterator { + file: self, + symbols: goblin::pe::symbol::SymbolIterator::default(), + } + } + + fn symbol_map(&self) -> SymbolMap<'data> { + // TODO: untested + let mut symbols: Vec<_> = self + .symbols() + .map(|(_, s)| s) + .filter(SymbolMap::filter) + .collect(); + symbols.sort_by_key(|x| x.address); + SymbolMap { symbols } + } + + fn has_debug_symbols(&self) -> bool { + for section in &self.coff.sections { + if let Ok(name) = section.name() { + if name == ".debug_info" { + return true; + } + } + } + false + } + + fn entry(&self) -> u64 { + 0 + } +} + +impl<'data, 'file> Iterator for CoffSegmentIterator<'data, 'file> { + type Item = CoffSegment<'data, 'file>; + + fn next(&mut self) -> Option<Self::Item> { + self.iter.next().map(|section| CoffSegment { + file: self.file, + section, + }) + } +} + +fn section_alignment(characteristics: u32) -> u64 { + match characteristics & pe::section_table::IMAGE_SCN_ALIGN_MASK { + pe::section_table::IMAGE_SCN_ALIGN_2BYTES => 2, + pe::section_table::IMAGE_SCN_ALIGN_4BYTES => 4, + pe::section_table::IMAGE_SCN_ALIGN_8BYTES => 8, + pe::section_table::IMAGE_SCN_ALIGN_16BYTES => 16, + pe::section_table::IMAGE_SCN_ALIGN_32BYTES => 32, + pe::section_table::IMAGE_SCN_ALIGN_64BYTES => 64, + pe::section_table::IMAGE_SCN_ALIGN_128BYTES => 128, + pe::section_table::IMAGE_SCN_ALIGN_256BYTES => 256, + pe::section_table::IMAGE_SCN_ALIGN_512BYTES => 512, + pe::section_table::IMAGE_SCN_ALIGN_1024BYTES => 1024, + pe::section_table::IMAGE_SCN_ALIGN_2048BYTES => 2048, + pe::section_table::IMAGE_SCN_ALIGN_4096BYTES => 4096, + pe::section_table::IMAGE_SCN_ALIGN_8192BYTES => 8192, + _ => 1, + } +} + +impl<'data, 'file> ObjectSegment<'data> for CoffSegment<'data, 'file> { + #[inline] + fn address(&self) -> u64 { + u64::from(self.section.virtual_address) + } + + #[inline] + fn size(&self) -> u64 { + u64::from(self.section.virtual_size) + } + + #[inline] + fn align(&self) -> u64 { + section_alignment(self.section.characteristics) + } + + #[inline] + fn file_range(&self) -> (u64, u64) { + ( + self.section.pointer_to_raw_data as u64, + self.section.size_of_raw_data as u64, + ) + } + + fn data(&self) -> &'data [u8] { + let offset = self.section.pointer_to_raw_data as usize; + let size = self.section.size_of_raw_data as usize; + &self.file.data[offset..][..size] + } + + fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]> { + read::data_range(self.data(), self.address(), address, size) + } + + #[inline] + fn name(&self) -> Option<&str> { + self.section.name().ok() + } +} + +impl<'data, 'file> Iterator for CoffSectionIterator<'data, 'file> { + type Item = CoffSection<'data, 'file>; + + fn next(&mut self) -> Option<Self::Item> { + self.iter.next().map(|(index, section)| CoffSection { + file: self.file, + index: SectionIndex(index), + section, + }) + } +} + +impl<'data, 'file> CoffSection<'data, 'file> { + fn raw_data(&self) -> &'data [u8] { + let offset = self.section.pointer_to_raw_data as usize; + let size = self.section.size_of_raw_data as usize; + &self.file.data[offset..][..size] + } +} + +impl<'data, 'file> ObjectSection<'data> for CoffSection<'data, 'file> { + type RelocationIterator = CoffRelocationIterator<'data, 'file>; + + #[inline] + fn index(&self) -> SectionIndex { + self.index + } + + #[inline] + fn address(&self) -> u64 { + u64::from(self.section.virtual_address) + } + + #[inline] + fn size(&self) -> u64 { + u64::from(self.section.size_of_raw_data) + } + + #[inline] + fn align(&self) -> u64 { + section_alignment(self.section.characteristics) + } + + #[inline] + fn file_range(&self) -> Option<(u64, u64)> { + Some(( + self.section.pointer_to_raw_data as u64, + self.section.size_of_raw_data as u64, + )) + } + + fn data(&self) -> Cow<'data, [u8]> { + Cow::from(self.raw_data()) + } + + fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]> { + read::data_range(self.raw_data(), self.address(), address, size) + } + + #[inline] + fn uncompressed_data(&self) -> Cow<'data, [u8]> { + self.data() + } + + fn name(&self) -> Option<&str> { + self.section.name().ok() + } + + #[inline] + fn segment_name(&self) -> Option<&str> { + None + } + + #[inline] + fn kind(&self) -> SectionKind { + if self.section.characteristics + & (pe::section_table::IMAGE_SCN_CNT_CODE | pe::section_table::IMAGE_SCN_MEM_EXECUTE) + != 0 + { + SectionKind::Text + } else if self.section.characteristics & pe::section_table::IMAGE_SCN_CNT_INITIALIZED_DATA + != 0 + { + if self.section.characteristics & pe::section_table::IMAGE_SCN_MEM_DISCARDABLE != 0 { + SectionKind::Other + } else if self.section.characteristics & pe::section_table::IMAGE_SCN_MEM_WRITE != 0 { + SectionKind::Data + } else { + SectionKind::ReadOnlyData + } + } else if self.section.characteristics & pe::section_table::IMAGE_SCN_CNT_UNINITIALIZED_DATA + != 0 + { + SectionKind::UninitializedData + } else if self.section.characteristics & pe::section_table::IMAGE_SCN_LNK_INFO != 0 { + SectionKind::Linker + } else { + SectionKind::Unknown + } + } + + fn relocations(&self) -> CoffRelocationIterator<'data, 'file> { + CoffRelocationIterator { + file: self.file, + relocations: self.section.relocations(self.file.data).unwrap_or_default(), + } + } +} + +impl<'data, 'file> fmt::Debug for CoffSymbolIterator<'data, 'file> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CoffSymbolIterator").finish() + } +} + +impl<'data, 'file> Iterator for CoffSymbolIterator<'data, 'file> { + type Item = (SymbolIndex, Symbol<'data>); + + fn next(&mut self) -> Option<Self::Item> { + self.symbols.next().map(|(index, name, symbol)| { + ( + SymbolIndex(index), + parse_symbol(index, name, &symbol, &self.file.coff), + ) + }) + } +} + +fn parse_symbol<'data>( + index: usize, + name: Option<&'data str>, + symbol: &pe::symbol::Symbol, + coff: &pe::Coff<'data>, +) -> Symbol<'data> { + let name = if symbol.is_file() { + coff.symbols + .aux_file(index + 1, symbol.number_of_aux_symbols as usize) + } else { + name.or_else(|| { + symbol.name_offset().and_then(|offset| { + coff.strings + .get(offset as usize) + .map(Result::ok) + .unwrap_or_default() + }) + }) + }; + let size = if symbol.is_function_definition() && symbol.number_of_aux_symbols > 0 { + coff.symbols + .aux_function_definition(index + 1) + .map(|aux| u64::from(aux.total_size)) + .unwrap_or(0) + } else { + 0 + }; + let kind = if symbol.is_section_definition() { + SymbolKind::Section + } else { + match symbol.storage_class { + pe::symbol::IMAGE_SYM_CLASS_SECTION => SymbolKind::Section, + pe::symbol::IMAGE_SYM_CLASS_FILE => SymbolKind::File, + pe::symbol::IMAGE_SYM_CLASS_LABEL => SymbolKind::Label, + pe::symbol::IMAGE_SYM_CLASS_EXTERNAL + | pe::symbol::IMAGE_SYM_CLASS_WEAK_EXTERNAL + | pe::symbol::IMAGE_SYM_CLASS_STATIC => { + if symbol.derived_type() == pe::symbol::IMAGE_SYM_DTYPE_FUNCTION { + SymbolKind::Text + } else { + SymbolKind::Data + } + } + _ => SymbolKind::Unknown, + } + }; + let section_index = if symbol.section_number <= 0 { + None + } else { + Some(SectionIndex(symbol.section_number as usize - 1)) + }; + let undefined = symbol.section_number == pe::symbol::IMAGE_SYM_UNDEFINED; + let weak = symbol.storage_class == pe::symbol::IMAGE_SYM_CLASS_WEAK_EXTERNAL; + let scope = match symbol.storage_class { + _ if undefined => SymbolScope::Unknown, + pe::symbol::IMAGE_SYM_CLASS_EXTERNAL + | pe::symbol::IMAGE_SYM_CLASS_EXTERNAL_DEF + | pe::symbol::IMAGE_SYM_CLASS_WEAK_EXTERNAL => { + // TODO: determine if symbol is exported + SymbolScope::Linkage + } + _ => SymbolScope::Compilation, + }; + Symbol { + name, + address: u64::from(symbol.value), + size, + kind, + section_index, + undefined, + weak, + scope, + } +} + +impl<'data, 'file> Iterator for CoffRelocationIterator<'data, 'file> { + type Item = (u64, Relocation); + + fn next(&mut self) -> Option<Self::Item> { + self.relocations.next().map(|relocation| { + let (kind, size, addend) = match self.file.coff.header.machine { + pe::header::COFF_MACHINE_X86 => match relocation.typ { + pe::relocation::IMAGE_REL_I386_DIR16 => (RelocationKind::Absolute, 16, 0), + pe::relocation::IMAGE_REL_I386_REL16 => (RelocationKind::Relative, 16, 0), + pe::relocation::IMAGE_REL_I386_DIR32 => (RelocationKind::Absolute, 32, 0), + pe::relocation::IMAGE_REL_I386_DIR32NB => (RelocationKind::ImageOffset, 32, 0), + pe::relocation::IMAGE_REL_I386_SECTION => (RelocationKind::SectionIndex, 16, 0), + pe::relocation::IMAGE_REL_I386_SECREL => (RelocationKind::SectionOffset, 32, 0), + pe::relocation::IMAGE_REL_I386_SECREL7 => (RelocationKind::SectionOffset, 7, 0), + pe::relocation::IMAGE_REL_I386_REL32 => (RelocationKind::Relative, 32, -4), + _ => (RelocationKind::Coff(relocation.typ), 0, 0), + }, + pe::header::COFF_MACHINE_X86_64 => match relocation.typ { + pe::relocation::IMAGE_REL_AMD64_ADDR64 => (RelocationKind::Absolute, 64, 0), + pe::relocation::IMAGE_REL_AMD64_ADDR32 => (RelocationKind::Absolute, 32, 0), + pe::relocation::IMAGE_REL_AMD64_ADDR32NB => { + (RelocationKind::ImageOffset, 32, 0) + } + pe::relocation::IMAGE_REL_AMD64_REL32 => (RelocationKind::Relative, 32, -4), + pe::relocation::IMAGE_REL_AMD64_REL32_1 => (RelocationKind::Relative, 32, -5), + pe::relocation::IMAGE_REL_AMD64_REL32_2 => (RelocationKind::Relative, 32, -6), + pe::relocation::IMAGE_REL_AMD64_REL32_3 => (RelocationKind::Relative, 32, -7), + pe::relocation::IMAGE_REL_AMD64_REL32_4 => (RelocationKind::Relative, 32, -8), + pe::relocation::IMAGE_REL_AMD64_REL32_5 => (RelocationKind::Relative, 32, -9), + pe::relocation::IMAGE_REL_AMD64_SECTION => { + (RelocationKind::SectionIndex, 16, 0) + } + pe::relocation::IMAGE_REL_AMD64_SECREL => { + (RelocationKind::SectionOffset, 32, 0) + } + pe::relocation::IMAGE_REL_AMD64_SECREL7 => { + (RelocationKind::SectionOffset, 7, 0) + } + _ => (RelocationKind::Coff(relocation.typ), 0, 0), + }, + _ => (RelocationKind::Coff(relocation.typ), 0, 0), + }; + let target = + RelocationTarget::Symbol(SymbolIndex(relocation.symbol_table_index as usize)); + ( + u64::from(relocation.virtual_address), + Relocation { + kind, + encoding: RelocationEncoding::Generic, + size, + target, + addend, + implicit_addend: true, + }, + ) + }) + } +} + +impl<'data, 'file> fmt::Debug for CoffRelocationIterator<'data, 'file> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CoffRelocationIterator").finish() + } +} diff --git a/third_party/rust/object/src/read/elf.rs b/third_party/rust/object/src/read/elf.rs new file mode 100644 index 0000000000..6af690756a --- /dev/null +++ b/third_party/rust/object/src/read/elf.rs @@ -0,0 +1,709 @@ +use crate::alloc::borrow::Cow; +use crate::alloc::fmt; +use crate::alloc::vec::Vec; +#[cfg(feature = "compression")] +use flate2::{Decompress, FlushDecompress}; +#[cfg(feature = "compression")] +use goblin::container; +use goblin::{elf, strtab}; +#[cfg(feature = "compression")] +use scroll::ctx::TryFromCtx; +use scroll::{self, Pread}; +use std::{iter, slice}; +use target_lexicon::{Aarch64Architecture, Architecture, ArmArchitecture}; + +use crate::read::{ + self, Object, ObjectSection, ObjectSegment, Relocation, RelocationEncoding, RelocationKind, + RelocationTarget, SectionIndex, SectionKind, Symbol, SymbolIndex, SymbolKind, SymbolMap, + SymbolScope, +}; + +/// An ELF object file. +#[derive(Debug)] +pub struct ElfFile<'data> { + elf: elf::Elf<'data>, + data: &'data [u8], +} + +impl<'data> ElfFile<'data> { + /// Get the ELF headers of the file. + // TODO: this is temporary to allow access to features this crate doesn't provide yet + #[inline] + pub fn elf(&self) -> &elf::Elf<'data> { + &self.elf + } + + /// Parse the raw ELF file data. + pub fn parse(data: &'data [u8]) -> Result<Self, &'static str> { + let elf = elf::Elf::parse(data).map_err(|_| "Could not parse ELF header")?; + Ok(ElfFile { elf, data }) + } + + fn raw_section_by_name<'file>( + &'file self, + section_name: &str, + ) -> Option<ElfSection<'data, 'file>> { + for (index, section) in self.elf.section_headers.iter().enumerate() { + if let Some(Ok(name)) = self.elf.shdr_strtab.get(section.sh_name) { + if name == section_name { + return Some(ElfSection { + file: self, + index: SectionIndex(index), + section, + }); + } + } + } + None + } + + #[cfg(feature = "compression")] + fn zdebug_section_by_name<'file>( + &'file self, + section_name: &str, + ) -> Option<ElfSection<'data, 'file>> { + if !section_name.starts_with(".debug_") { + return None; + } + self.raw_section_by_name(&format!(".zdebug_{}", §ion_name[7..])) + } + + #[cfg(not(feature = "compression"))] + fn zdebug_section_by_name<'file>( + &'file self, + _section_name: &str, + ) -> Option<ElfSection<'data, 'file>> { + None + } +} + +impl<'data, 'file> Object<'data, 'file> for ElfFile<'data> +where + 'data: 'file, +{ + type Segment = ElfSegment<'data, 'file>; + type SegmentIterator = ElfSegmentIterator<'data, 'file>; + type Section = ElfSection<'data, 'file>; + type SectionIterator = ElfSectionIterator<'data, 'file>; + type SymbolIterator = ElfSymbolIterator<'data, 'file>; + + fn architecture(&self) -> Architecture { + match self.elf.header.e_machine { + elf::header::EM_ARM => Architecture::Arm(ArmArchitecture::Arm), + elf::header::EM_AARCH64 => Architecture::Aarch64(Aarch64Architecture::Aarch64), + elf::header::EM_386 => Architecture::I386, + elf::header::EM_X86_64 => Architecture::X86_64, + elf::header::EM_MIPS => Architecture::Mips, + _ => Architecture::Unknown, + } + } + + #[inline] + fn is_little_endian(&self) -> bool { + self.elf.little_endian + } + + #[inline] + fn is_64(&self) -> bool { + self.elf.is_64 + } + + fn segments(&'file self) -> ElfSegmentIterator<'data, 'file> { + ElfSegmentIterator { + file: self, + iter: self.elf.program_headers.iter(), + } + } + + fn section_by_name(&'file self, section_name: &str) -> Option<ElfSection<'data, 'file>> { + self.raw_section_by_name(section_name) + .or_else(|| self.zdebug_section_by_name(section_name)) + } + + fn section_by_index(&'file self, index: SectionIndex) -> Option<ElfSection<'data, 'file>> { + self.elf + .section_headers + .get(index.0) + .map(|section| ElfSection { + file: self, + index, + section, + }) + } + + fn sections(&'file self) -> ElfSectionIterator<'data, 'file> { + ElfSectionIterator { + file: self, + iter: self.elf.section_headers.iter().enumerate(), + } + } + + fn symbol_by_index(&self, index: SymbolIndex) -> Option<Symbol<'data>> { + self.elf + .syms + .get(index.0) + .map(|symbol| parse_symbol(index.0, &symbol, &self.elf.strtab)) + } + + fn symbols(&'file self) -> ElfSymbolIterator<'data, 'file> { + ElfSymbolIterator { + strtab: &self.elf.strtab, + symbols: self.elf.syms.iter().enumerate(), + } + } + + fn dynamic_symbols(&'file self) -> ElfSymbolIterator<'data, 'file> { + ElfSymbolIterator { + strtab: &self.elf.dynstrtab, + symbols: self.elf.dynsyms.iter().enumerate(), + } + } + + fn symbol_map(&self) -> SymbolMap<'data> { + let mut symbols: Vec<_> = self + .symbols() + .map(|(_, s)| s) + .filter(SymbolMap::filter) + .collect(); + symbols.sort_by_key(|x| x.address); + SymbolMap { symbols } + } + + fn has_debug_symbols(&self) -> bool { + for header in &self.elf.section_headers { + if let Some(Ok(name)) = self.elf.shdr_strtab.get(header.sh_name) { + if name == ".debug_info" || name == ".zdebug_info" { + return true; + } + } + } + false + } + + fn build_id(&self) -> Option<&'data [u8]> { + if let Some(mut notes) = self.elf.iter_note_headers(self.data) { + while let Some(Ok(note)) = notes.next() { + if note.n_type == elf::note::NT_GNU_BUILD_ID { + return Some(note.desc); + } + } + } + if let Some(mut notes) = self + .elf + .iter_note_sections(self.data, Some(".note.gnu.build-id")) + { + while let Some(Ok(note)) = notes.next() { + if note.n_type == elf::note::NT_GNU_BUILD_ID { + return Some(note.desc); + } + } + } + None + } + + fn gnu_debuglink(&self) -> Option<(&'data [u8], u32)> { + if let Some(Cow::Borrowed(data)) = self.section_data_by_name(".gnu_debuglink") { + if let Some(filename_len) = data.iter().position(|x| *x == 0) { + let filename = &data[..filename_len]; + // Round to 4 byte alignment after null terminator. + let offset = (filename_len + 1 + 3) & !3; + if offset + 4 <= data.len() { + let endian = if self.is_little_endian() { + scroll::LE + } else { + scroll::BE + }; + let crc: u32 = data.pread_with(offset, endian).unwrap(); + return Some((filename, crc)); + } + } + } + None + } + + fn entry(&self) -> u64 { + self.elf.entry + } +} + +/// An iterator over the segments of an `ElfFile`. +#[derive(Debug)] +pub struct ElfSegmentIterator<'data, 'file> +where + 'data: 'file, +{ + file: &'file ElfFile<'data>, + iter: slice::Iter<'file, elf::ProgramHeader>, +} + +impl<'data, 'file> Iterator for ElfSegmentIterator<'data, 'file> { + type Item = ElfSegment<'data, 'file>; + + fn next(&mut self) -> Option<Self::Item> { + while let Some(segment) = self.iter.next() { + if segment.p_type == elf::program_header::PT_LOAD { + return Some(ElfSegment { + file: self.file, + segment, + }); + } + } + None + } +} + +/// A segment of an `ElfFile`. +#[derive(Debug)] +pub struct ElfSegment<'data, 'file> +where + 'data: 'file, +{ + file: &'file ElfFile<'data>, + segment: &'file elf::ProgramHeader, +} + +impl<'data, 'file> ObjectSegment<'data> for ElfSegment<'data, 'file> { + #[inline] + fn address(&self) -> u64 { + self.segment.p_vaddr + } + + #[inline] + fn size(&self) -> u64 { + self.segment.p_memsz + } + + #[inline] + fn align(&self) -> u64 { + self.segment.p_align + } + + #[inline] + fn file_range(&self) -> (u64, u64) { + (self.segment.p_offset, self.segment.p_filesz) + } + + fn data(&self) -> &'data [u8] { + &self.file.data[self.segment.p_offset as usize..][..self.segment.p_filesz as usize] + } + + fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]> { + read::data_range(self.data(), self.address(), address, size) + } + + #[inline] + fn name(&self) -> Option<&str> { + None + } +} + +/// An iterator over the sections of an `ElfFile`. +#[derive(Debug)] +pub struct ElfSectionIterator<'data, 'file> +where + 'data: 'file, +{ + file: &'file ElfFile<'data>, + iter: iter::Enumerate<slice::Iter<'file, elf::SectionHeader>>, +} + +impl<'data, 'file> Iterator for ElfSectionIterator<'data, 'file> { + type Item = ElfSection<'data, 'file>; + + 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 `ElfFile`. +#[derive(Debug)] +pub struct ElfSection<'data, 'file> +where + 'data: 'file, +{ + file: &'file ElfFile<'data>, + index: SectionIndex, + section: &'file elf::SectionHeader, +} + +impl<'data, 'file> ElfSection<'data, 'file> { + fn raw_offset(&self) -> Option<(u64, u64)> { + if self.section.sh_type == elf::section_header::SHT_NOBITS { + None + } else { + Some((self.section.sh_offset, self.section.sh_size)) + } + } + + fn raw_data(&self) -> &'data [u8] { + if let Some((offset, size)) = self.raw_offset() { + &self.file.data[offset as usize..][..size as usize] + } else { + &[] + } + } + + #[cfg(feature = "compression")] + fn maybe_decompress_data(&self) -> Option<Cow<'data, [u8]>> { + if (self.section.sh_flags & u64::from(elf::section_header::SHF_COMPRESSED)) == 0 { + return None; + } + + let container = match self.file.elf.header.container() { + Ok(c) => c, + Err(_) => return None, + }; + let endianness = match self.file.elf.header.endianness() { + Ok(e) => e, + Err(_) => return None, + }; + let ctx = container::Ctx::new(container, endianness); + let data = self.raw_data(); + let (compression_type, uncompressed_size, compressed_data) = + match elf::compression_header::CompressionHeader::try_from_ctx(&data, ctx) { + Ok((chdr, size)) => (chdr.ch_type, chdr.ch_size, &data[size..]), + Err(_) => return None, + }; + if compression_type != elf::compression_header::ELFCOMPRESS_ZLIB { + return None; + } + + let mut decompressed = Vec::with_capacity(uncompressed_size as usize); + let mut decompress = Decompress::new(true); + if decompress + .decompress_vec(compressed_data, &mut decompressed, FlushDecompress::Finish) + .is_err() + { + return None; + } + Some(Cow::Owned(decompressed)) + } + + /// Try GNU-style "ZLIB" header decompression. + #[cfg(feature = "compression")] + fn maybe_decompress_data_gnu(&self) -> Option<Cow<'data, [u8]>> { + let name = match self.name() { + Some(name) => name, + None => return None, + }; + if !name.starts_with(".zdebug_") { + return None; + } + let data = self.raw_data(); + // Assume ZLIB-style uncompressed data is no more than 4GB to avoid accidentally + // huge allocations. This also reduces the chance of accidentally matching on a + // .debug_str that happens to start with "ZLIB". + if data.len() < 12 || &data[..8] != b"ZLIB\0\0\0\0" { + return None; + } + let uncompressed_size: u32 = data.pread_with(8, scroll::BE).unwrap(); + let mut decompressed = Vec::with_capacity(uncompressed_size as usize); + let mut decompress = Decompress::new(true); + if decompress + .decompress_vec(&data[12..], &mut decompressed, FlushDecompress::Finish) + .is_err() + { + return None; + } + Some(Cow::Owned(decompressed)) + } +} + +impl<'data, 'file> ObjectSection<'data> for ElfSection<'data, 'file> { + type RelocationIterator = ElfRelocationIterator<'data, 'file>; + + #[inline] + fn index(&self) -> SectionIndex { + self.index + } + + #[inline] + fn address(&self) -> u64 { + self.section.sh_addr + } + + #[inline] + fn size(&self) -> u64 { + self.section.sh_size + } + + #[inline] + fn align(&self) -> u64 { + self.section.sh_addralign + } + + #[inline] + fn file_range(&self) -> Option<(u64, u64)> { + self.raw_offset() + } + + #[inline] + fn data(&self) -> Cow<'data, [u8]> { + Cow::from(self.raw_data()) + } + + fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]> { + read::data_range(self.raw_data(), self.address(), address, size) + } + + #[cfg(feature = "compression")] + fn uncompressed_data(&self) -> Cow<'data, [u8]> { + self.maybe_decompress_data() + .or_else(|| self.maybe_decompress_data_gnu()) + .unwrap_or_else(|| self.data()) + } + + #[cfg(not(feature = "compression"))] + #[inline] + fn uncompressed_data(&self) -> Cow<'data, [u8]> { + self.data() + } + + fn name(&self) -> Option<&str> { + self.file + .elf + .shdr_strtab + .get(self.section.sh_name) + .and_then(Result::ok) + } + + #[inline] + fn segment_name(&self) -> Option<&str> { + None + } + + fn kind(&self) -> SectionKind { + match self.section.sh_type { + elf::section_header::SHT_PROGBITS => { + if self.section.sh_flags & u64::from(elf::section_header::SHF_ALLOC) != 0 { + if self.section.sh_flags & u64::from(elf::section_header::SHF_EXECINSTR) != 0 { + SectionKind::Text + } else if self.section.sh_flags & u64::from(elf::section_header::SHF_TLS) != 0 { + SectionKind::Tls + } else if self.section.sh_flags & u64::from(elf::section_header::SHF_WRITE) != 0 + { + SectionKind::Data + } else if self.section.sh_flags & u64::from(elf::section_header::SHF_STRINGS) + != 0 + { + SectionKind::ReadOnlyString + } else { + SectionKind::ReadOnlyData + } + } else if self.section.sh_flags & u64::from(elf::section_header::SHF_STRINGS) != 0 { + SectionKind::OtherString + } else { + SectionKind::Other + } + } + elf::section_header::SHT_NOBITS => { + if self.section.sh_flags & u64::from(elf::section_header::SHF_TLS) != 0 { + SectionKind::UninitializedTls + } else { + SectionKind::UninitializedData + } + } + elf::section_header::SHT_NULL + | elf::section_header::SHT_SYMTAB + | elf::section_header::SHT_STRTAB + | elf::section_header::SHT_RELA + | elf::section_header::SHT_HASH + | elf::section_header::SHT_DYNAMIC + | elf::section_header::SHT_REL + | elf::section_header::SHT_DYNSYM => SectionKind::Metadata, + _ => { + // TODO: maybe add more specialised kinds based on sh_type (e.g. Unwind) + SectionKind::Unknown + } + } + } + + fn relocations(&self) -> ElfRelocationIterator<'data, 'file> { + ElfRelocationIterator { + section_index: self.index, + file: self.file, + sections: self.file.elf.shdr_relocs.iter(), + relocations: None, + } + } +} + +/// An iterator over the symbols of an `ElfFile`. +pub struct ElfSymbolIterator<'data, 'file> +where + 'data: 'file, +{ + strtab: &'file strtab::Strtab<'data>, + symbols: iter::Enumerate<elf::sym::SymIterator<'data>>, +} + +impl<'data, 'file> fmt::Debug for ElfSymbolIterator<'data, 'file> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ElfSymbolIterator").finish() + } +} + +impl<'data, 'file> Iterator for ElfSymbolIterator<'data, 'file> { + type Item = (SymbolIndex, Symbol<'data>); + + fn next(&mut self) -> Option<Self::Item> { + self.symbols.next().map(|(index, symbol)| { + ( + SymbolIndex(index), + parse_symbol(index, &symbol, self.strtab), + ) + }) + } +} + +fn parse_symbol<'data>( + index: usize, + symbol: &elf::sym::Sym, + strtab: &strtab::Strtab<'data>, +) -> Symbol<'data> { + let name = strtab.get(symbol.st_name).and_then(Result::ok); + let kind = match elf::sym::st_type(symbol.st_info) { + elf::sym::STT_NOTYPE if index == 0 => SymbolKind::Null, + elf::sym::STT_OBJECT => SymbolKind::Data, + elf::sym::STT_FUNC => SymbolKind::Text, + elf::sym::STT_SECTION => SymbolKind::Section, + elf::sym::STT_FILE => SymbolKind::File, + elf::sym::STT_COMMON => SymbolKind::Common, + elf::sym::STT_TLS => SymbolKind::Tls, + _ => SymbolKind::Unknown, + }; + let undefined = symbol.st_shndx == elf::section_header::SHN_UNDEF as usize; + let section_index = + if undefined || symbol.st_shndx >= elf::section_header::SHN_LORESERVE as usize { + None + } else { + Some(SectionIndex(symbol.st_shndx)) + }; + let weak = symbol.st_bind() == elf::sym::STB_WEAK; + let scope = match symbol.st_bind() { + _ if undefined => SymbolScope::Unknown, + elf::sym::STB_LOCAL => SymbolScope::Compilation, + elf::sym::STB_GLOBAL | elf::sym::STB_WEAK => { + if symbol.st_visibility() == elf::sym::STV_HIDDEN { + SymbolScope::Linkage + } else { + SymbolScope::Dynamic + } + } + _ => SymbolScope::Unknown, + }; + Symbol { + name, + address: symbol.st_value, + size: symbol.st_size, + kind, + section_index, + undefined, + weak, + scope, + } +} + +/// An iterator over the relocations in an `ElfSection`. +pub struct ElfRelocationIterator<'data, 'file> +where + 'data: 'file, +{ + /// The index of the section that the relocations apply to. + section_index: SectionIndex, + file: &'file ElfFile<'data>, + sections: slice::Iter<'file, (elf::ShdrIdx, elf::RelocSection<'data>)>, + relocations: Option<elf::reloc::RelocIterator<'data>>, +} + +impl<'data, 'file> Iterator for ElfRelocationIterator<'data, 'file> { + type Item = (u64, Relocation); + + fn next(&mut self) -> Option<Self::Item> { + loop { + if let Some(ref mut relocations) = self.relocations { + if let Some(reloc) = relocations.next() { + let mut encoding = RelocationEncoding::Generic; + let (kind, size) = match self.file.elf.header.e_machine { + elf::header::EM_ARM => match reloc.r_type { + elf::reloc::R_ARM_ABS32 => (RelocationKind::Absolute, 32), + _ => (RelocationKind::Elf(reloc.r_type), 0), + }, + elf::header::EM_AARCH64 => match reloc.r_type { + elf::reloc::R_AARCH64_ABS64 => (RelocationKind::Absolute, 64), + elf::reloc::R_AARCH64_ABS32 => (RelocationKind::Absolute, 32), + elf::reloc::R_AARCH64_ABS16 => (RelocationKind::Absolute, 16), + elf::reloc::R_AARCH64_PREL64 => (RelocationKind::Relative, 64), + elf::reloc::R_AARCH64_PREL32 => (RelocationKind::Relative, 32), + elf::reloc::R_AARCH64_PREL16 => (RelocationKind::Relative, 16), + _ => (RelocationKind::Elf(reloc.r_type), 0), + }, + elf::header::EM_386 => match reloc.r_type { + elf::reloc::R_386_32 => (RelocationKind::Absolute, 32), + elf::reloc::R_386_PC32 => (RelocationKind::Relative, 32), + elf::reloc::R_386_GOT32 => (RelocationKind::Got, 32), + elf::reloc::R_386_PLT32 => (RelocationKind::PltRelative, 32), + elf::reloc::R_386_GOTOFF => (RelocationKind::GotBaseOffset, 32), + elf::reloc::R_386_GOTPC => (RelocationKind::GotBaseRelative, 32), + elf::reloc::R_386_16 => (RelocationKind::Absolute, 16), + elf::reloc::R_386_PC16 => (RelocationKind::Relative, 16), + elf::reloc::R_386_8 => (RelocationKind::Absolute, 8), + elf::reloc::R_386_PC8 => (RelocationKind::Relative, 8), + _ => (RelocationKind::Elf(reloc.r_type), 0), + }, + elf::header::EM_X86_64 => match reloc.r_type { + elf::reloc::R_X86_64_64 => (RelocationKind::Absolute, 64), + elf::reloc::R_X86_64_PC32 => (RelocationKind::Relative, 32), + elf::reloc::R_X86_64_GOT32 => (RelocationKind::Got, 32), + elf::reloc::R_X86_64_PLT32 => (RelocationKind::PltRelative, 32), + elf::reloc::R_X86_64_GOTPCREL => (RelocationKind::GotRelative, 32), + elf::reloc::R_X86_64_32 => (RelocationKind::Absolute, 32), + elf::reloc::R_X86_64_32S => { + encoding = RelocationEncoding::X86Signed; + (RelocationKind::Absolute, 32) + } + elf::reloc::R_X86_64_16 => (RelocationKind::Absolute, 16), + elf::reloc::R_X86_64_PC16 => (RelocationKind::Relative, 16), + elf::reloc::R_X86_64_8 => (RelocationKind::Absolute, 8), + elf::reloc::R_X86_64_PC8 => (RelocationKind::Relative, 8), + _ => (RelocationKind::Elf(reloc.r_type), 0), + }, + _ => (RelocationKind::Elf(reloc.r_type), 0), + }; + let target = RelocationTarget::Symbol(SymbolIndex(reloc.r_sym as usize)); + return Some(( + reloc.r_offset, + Relocation { + kind, + encoding, + size, + target, + addend: reloc.r_addend.unwrap_or(0), + implicit_addend: reloc.r_addend.is_none(), + }, + )); + } + } + match self.sections.next() { + None => return None, + Some((index, relocs)) => { + let section = &self.file.elf.section_headers[*index]; + if section.sh_info as usize == self.section_index.0 { + self.relocations = Some(relocs.into_iter()); + } + // TODO: do we need to return section.sh_link? + } + } + } + } +} + +impl<'data, 'file> fmt::Debug for ElfRelocationIterator<'data, 'file> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ElfRelocationIterator").finish() + } +} diff --git a/third_party/rust/object/src/read/macho.rs b/third_party/rust/object/src/read/macho.rs new file mode 100644 index 0000000000..ce54938a10 --- /dev/null +++ b/third_party/rust/object/src/read/macho.rs @@ -0,0 +1,615 @@ +use crate::alloc::borrow::Cow; +use crate::alloc::vec::Vec; +use goblin::container; +use goblin::mach; +use goblin::mach::load_command::CommandVariant; +use std::{fmt, iter, ops, slice}; +use target_lexicon::{Aarch64Architecture, Architecture, ArmArchitecture}; +use uuid::Uuid; + +use crate::read::{ + self, Object, ObjectSection, ObjectSegment, Relocation, RelocationEncoding, RelocationKind, + RelocationTarget, SectionIndex, SectionKind, Symbol, SymbolIndex, SymbolKind, SymbolMap, + SymbolScope, +}; + +/// A Mach-O object file. +#[derive(Debug)] +pub struct MachOFile<'data> { + macho: mach::MachO<'data>, + data: &'data [u8], + ctx: container::Ctx, + sections: Vec<MachOSectionInternal<'data>>, +} + +impl<'data> MachOFile<'data> { + /// Get the Mach-O headers of the file. + // TODO: this is temporary to allow access to features this crate doesn't provide yet + #[inline] + pub fn macho(&self) -> &mach::MachO<'data> { + &self.macho + } + + /// Parse the raw Mach-O file data. + pub fn parse(data: &'data [u8]) -> Result<Self, &'static str> { + let (_magic, ctx) = + mach::parse_magic_and_ctx(data, 0).map_err(|_| "Could not parse Mach-O magic")?; + let ctx = ctx.ok_or("Invalid Mach-O magic")?; + let macho = mach::MachO::parse(data, 0).map_err(|_| "Could not parse Mach-O header")?; + // Build a list of sections to make some operations more efficient. + let mut sections = Vec::new(); + 'segments: for segment in &macho.segments { + for section_result in segment { + if let Ok((section, data)) = section_result { + sections.push(MachOSectionInternal::parse(section, data)); + } else { + break 'segments; + } + } + } + Ok(MachOFile { + macho, + data, + ctx, + sections, + }) + } + + /// Return the section at the given index. + #[inline] + fn section_internal(&self, index: SectionIndex) -> Option<&MachOSectionInternal<'data>> { + index + .0 + .checked_sub(1) + .and_then(|index| self.sections.get(index)) + } +} + +impl<'data, 'file> Object<'data, 'file> for MachOFile<'data> +where + 'data: 'file, +{ + type Segment = MachOSegment<'data, 'file>; + type SegmentIterator = MachOSegmentIterator<'data, 'file>; + type Section = MachOSection<'data, 'file>; + type SectionIterator = MachOSectionIterator<'data, 'file>; + type SymbolIterator = MachOSymbolIterator<'data, 'file>; + + fn architecture(&self) -> Architecture { + match self.macho.header.cputype { + mach::cputype::CPU_TYPE_ARM => Architecture::Arm(ArmArchitecture::Arm), + mach::cputype::CPU_TYPE_ARM64 => Architecture::Aarch64(Aarch64Architecture::Aarch64), + mach::cputype::CPU_TYPE_X86 => Architecture::I386, + mach::cputype::CPU_TYPE_X86_64 => Architecture::X86_64, + mach::cputype::CPU_TYPE_MIPS => Architecture::Mips, + _ => Architecture::Unknown, + } + } + + #[inline] + fn is_little_endian(&self) -> bool { + self.macho.little_endian + } + + #[inline] + fn is_64(&self) -> bool { + self.macho.is_64 + } + + fn segments(&'file self) -> MachOSegmentIterator<'data, 'file> { + MachOSegmentIterator { + segments: self.macho.segments.iter(), + } + } + + fn section_by_name(&'file self, section_name: &str) -> Option<MachOSection<'data, 'file>> { + // Translate the "." prefix to the "__" prefix used by OSX/Mach-O, eg + // ".debug_info" to "__debug_info". + let system_section = section_name.starts_with('.'); + let cmp_section_name = |section: &MachOSection| { + section + .name() + .map(|name| { + section_name == name + || (system_section + && name.starts_with("__") + && §ion_name[1..] == &name[2..]) + }) + .unwrap_or(false) + }; + + self.sections().find(cmp_section_name) + } + + fn section_by_index(&'file self, index: SectionIndex) -> Option<MachOSection<'data, 'file>> { + self.section_internal(index) + .map(|_| MachOSection { file: self, index }) + } + + fn sections(&'file self) -> MachOSectionIterator<'data, 'file> { + MachOSectionIterator { + file: self, + iter: 0..self.sections.len(), + } + } + + fn symbol_by_index(&self, index: SymbolIndex) -> Option<Symbol<'data>> { + self.macho + .symbols + .as_ref() + .and_then(|symbols| symbols.get(index.0).ok()) + .and_then(|(name, nlist)| parse_symbol(self, name, &nlist)) + } + + fn symbols(&'file self) -> MachOSymbolIterator<'data, 'file> { + let symbols = match self.macho.symbols { + Some(ref symbols) => symbols.into_iter(), + None => mach::symbols::SymbolIterator::default(), + } + .enumerate(); + + MachOSymbolIterator { + file: self, + symbols, + } + } + + fn dynamic_symbols(&'file self) -> MachOSymbolIterator<'data, 'file> { + // The LC_DYSYMTAB command contains indices into the same symbol + // table as the LC_SYMTAB command, so return all of them. + self.symbols() + } + + fn symbol_map(&self) -> SymbolMap<'data> { + let mut symbols: Vec<_> = self.symbols().map(|(_, s)| s).collect(); + + // Add symbols for the end of each section. + for section in self.sections() { + symbols.push(Symbol { + name: None, + address: section.address() + section.size(), + size: 0, + kind: SymbolKind::Section, + section_index: None, + undefined: false, + weak: false, + scope: SymbolScope::Compilation, + }); + } + + // Calculate symbol sizes by sorting and finding the next symbol. + symbols.sort_by(|a, b| { + a.address.cmp(&b.address).then_with(|| { + // Place the end of section symbols last. + (a.kind == SymbolKind::Section).cmp(&(b.kind == SymbolKind::Section)) + }) + }); + + for i in 0..symbols.len() { + let (before, after) = symbols.split_at_mut(i + 1); + let symbol = &mut before[i]; + if symbol.kind != SymbolKind::Section { + if let Some(next) = after + .iter() + .skip_while(|x| x.kind != SymbolKind::Section && x.address == symbol.address) + .next() + { + symbol.size = next.address - symbol.address; + } + } + } + + symbols.retain(SymbolMap::filter); + SymbolMap { symbols } + } + + fn has_debug_symbols(&self) -> bool { + self.section_data_by_name(".debug_info").is_some() + } + + fn mach_uuid(&self) -> Option<Uuid> { + // Return the UUID from the `LC_UUID` load command, if one is present. + self.macho + .load_commands + .iter() + .filter_map(|lc| { + match lc.command { + CommandVariant::Uuid(ref cmd) => { + //TODO: Uuid should have a `from_array` method that can't fail. + Uuid::from_slice(&cmd.uuid).ok() + } + _ => None, + } + }) + .nth(0) + } + + fn entry(&self) -> u64 { + self.macho.entry + } +} + +/// An iterator over the segments of a `MachOFile`. +#[derive(Debug)] +pub struct MachOSegmentIterator<'data, 'file> +where + 'data: 'file, +{ + segments: slice::Iter<'file, mach::segment::Segment<'data>>, +} + +impl<'data, 'file> Iterator for MachOSegmentIterator<'data, 'file> { + type Item = MachOSegment<'data, 'file>; + + fn next(&mut self) -> Option<Self::Item> { + self.segments.next().map(|segment| MachOSegment { segment }) + } +} + +/// A segment of a `MachOFile`. +#[derive(Debug)] +pub struct MachOSegment<'data, 'file> +where + 'data: 'file, +{ + segment: &'file mach::segment::Segment<'data>, +} + +impl<'data, 'file> ObjectSegment<'data> for MachOSegment<'data, 'file> { + #[inline] + fn address(&self) -> u64 { + self.segment.vmaddr + } + + #[inline] + fn size(&self) -> u64 { + self.segment.vmsize + } + + #[inline] + fn align(&self) -> u64 { + // Page size. + 0x1000 + } + + #[inline] + fn file_range(&self) -> (u64, u64) { + (self.segment.fileoff, self.segment.filesize) + } + + #[inline] + fn data(&self) -> &'data [u8] { + self.segment.data + } + + fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]> { + read::data_range(self.data(), self.address(), address, size) + } + + #[inline] + fn name(&self) -> Option<&str> { + self.segment.name().ok() + } +} + +/// An iterator over the sections of a `MachOFile`. +pub struct MachOSectionIterator<'data, 'file> +where + 'data: 'file, +{ + file: &'file MachOFile<'data>, + iter: ops::Range<usize>, +} + +impl<'data, 'file> fmt::Debug for MachOSectionIterator<'data, 'file> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // It's painful to do much better than this + f.debug_struct("MachOSectionIterator").finish() + } +} + +impl<'data, 'file> Iterator for MachOSectionIterator<'data, 'file> { + type Item = MachOSection<'data, 'file>; + + fn next(&mut self) -> Option<Self::Item> { + self.iter.next().map(|index| MachOSection { + file: self.file, + index: SectionIndex(index + 1), + }) + } +} + +/// A section of a `MachOFile`. +#[derive(Debug)] +pub struct MachOSection<'data, 'file> +where + 'data: 'file, +{ + file: &'file MachOFile<'data>, + index: SectionIndex, +} + +impl<'data, 'file> MachOSection<'data, 'file> { + #[inline] + fn internal(&self) -> &'file MachOSectionInternal<'data> { + // We ensure the index is always valid. + &self.file.section_internal(self.index).unwrap() + } +} + +impl<'data, 'file> ObjectSection<'data> for MachOSection<'data, 'file> { + type RelocationIterator = MachORelocationIterator<'data, 'file>; + + #[inline] + fn index(&self) -> SectionIndex { + self.index + } + + #[inline] + fn address(&self) -> u64 { + self.internal().section.addr + } + + #[inline] + fn size(&self) -> u64 { + self.internal().section.size + } + + #[inline] + fn align(&self) -> u64 { + 1 << self.internal().section.align + } + + #[inline] + fn file_range(&self) -> Option<(u64, u64)> { + let internal = &self.internal().section; + Some((internal.offset as u64, internal.size)) + } + + #[inline] + fn data(&self) -> Cow<'data, [u8]> { + Cow::from(self.internal().data) + } + + fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]> { + read::data_range(self.internal().data, self.address(), address, size) + } + + #[inline] + fn uncompressed_data(&self) -> Cow<'data, [u8]> { + // TODO: does MachO support compression? + self.data() + } + + #[inline] + fn name(&self) -> Option<&str> { + self.internal().section.name().ok() + } + + #[inline] + fn segment_name(&self) -> Option<&str> { + self.internal().section.segname().ok() + } + + fn kind(&self) -> SectionKind { + self.internal().kind + } + + fn relocations(&self) -> MachORelocationIterator<'data, 'file> { + MachORelocationIterator { + file: self.file, + relocations: self + .internal() + .section + .iter_relocations(self.file.data, self.file.ctx), + } + } +} + +#[derive(Debug)] +struct MachOSectionInternal<'data> { + section: mach::segment::Section, + data: mach::segment::SectionData<'data>, + kind: SectionKind, +} + +impl<'data> MachOSectionInternal<'data> { + fn parse(section: mach::segment::Section, data: mach::segment::SectionData<'data>) -> Self { + let kind = if let (Ok(segname), Ok(name)) = (section.segname(), section.name()) { + match (segname, name) { + ("__TEXT", "__text") => SectionKind::Text, + ("__TEXT", "__const") => SectionKind::ReadOnlyData, + ("__TEXT", "__cstring") => SectionKind::ReadOnlyString, + ("__TEXT", "__eh_frame") => SectionKind::ReadOnlyData, + ("__TEXT", "__gcc_except_tab") => SectionKind::ReadOnlyData, + ("__DATA", "__data") => SectionKind::Data, + ("__DATA", "__const") => SectionKind::ReadOnlyData, + ("__DATA", "__bss") => SectionKind::UninitializedData, + ("__DATA", "__thread_data") => SectionKind::Tls, + ("__DATA", "__thread_bss") => SectionKind::UninitializedTls, + ("__DATA", "__thread_vars") => SectionKind::TlsVariables, + ("__DWARF", _) => SectionKind::Debug, + _ => SectionKind::Unknown, + } + } else { + SectionKind::Unknown + }; + MachOSectionInternal { + section, + data, + kind, + } + } +} + +/// An iterator over the symbols of a `MachOFile`. +pub struct MachOSymbolIterator<'data, 'file> { + file: &'file MachOFile<'data>, + symbols: iter::Enumerate<mach::symbols::SymbolIterator<'data>>, +} + +impl<'data, 'file> fmt::Debug for MachOSymbolIterator<'data, 'file> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("MachOSymbolIterator").finish() + } +} + +impl<'data, 'file> Iterator for MachOSymbolIterator<'data, 'file> { + type Item = (SymbolIndex, Symbol<'data>); + + fn next(&mut self) -> Option<Self::Item> { + while let Some((index, Ok((name, nlist)))) = self.symbols.next() { + if let Some(symbol) = parse_symbol(self.file, name, &nlist) { + return Some((SymbolIndex(index), symbol)); + } + } + None + } +} + +fn parse_symbol<'data>( + file: &MachOFile<'data>, + name: &'data str, + nlist: &mach::symbols::Nlist, +) -> Option<Symbol<'data>> { + if nlist.n_type & mach::symbols::N_STAB != 0 { + return None; + } + let n_type = nlist.n_type & mach::symbols::NLIST_TYPE_MASK; + let section_index = if n_type == mach::symbols::N_SECT { + if nlist.n_sect == 0 { + None + } else { + Some(SectionIndex(nlist.n_sect)) + } + } else { + // TODO: better handling for other n_type values + None + }; + let kind = section_index + .and_then(|index| file.section_internal(index)) + .map(|section| match section.kind { + SectionKind::Text => SymbolKind::Text, + SectionKind::Data + | SectionKind::ReadOnlyData + | SectionKind::ReadOnlyString + | SectionKind::UninitializedData => SymbolKind::Data, + SectionKind::Tls | SectionKind::UninitializedTls | SectionKind::TlsVariables => { + SymbolKind::Tls + } + _ => SymbolKind::Unknown, + }) + .unwrap_or(SymbolKind::Unknown); + let undefined = nlist.is_undefined(); + let weak = nlist.is_weak(); + let scope = if undefined { + SymbolScope::Unknown + } else if nlist.n_type & mach::symbols::N_EXT == 0 { + SymbolScope::Compilation + } else if nlist.n_type & mach::symbols::N_PEXT != 0 { + SymbolScope::Linkage + } else { + SymbolScope::Dynamic + }; + Some(Symbol { + name: Some(name), + address: nlist.n_value, + // Only calculated for symbol maps + size: 0, + kind, + section_index, + undefined, + weak, + scope, + }) +} + +/// An iterator over the relocations in an `MachOSection`. +pub struct MachORelocationIterator<'data, 'file> +where + 'data: 'file, +{ + file: &'file MachOFile<'data>, + relocations: mach::segment::RelocationIterator<'data>, +} + +impl<'data, 'file> Iterator for MachORelocationIterator<'data, 'file> { + type Item = (u64, Relocation); + + fn next(&mut self) -> Option<Self::Item> { + self.relocations.next()?.ok().map(|reloc| { + let mut encoding = RelocationEncoding::Generic; + let kind = match self.file.macho.header.cputype { + mach::cputype::CPU_TYPE_ARM => match (reloc.r_type(), reloc.r_pcrel()) { + (mach::relocation::ARM_RELOC_VANILLA, 0) => RelocationKind::Absolute, + _ => RelocationKind::MachO { + value: reloc.r_type(), + relative: reloc.is_pic(), + }, + }, + mach::cputype::CPU_TYPE_ARM64 => match (reloc.r_type(), reloc.r_pcrel()) { + (mach::relocation::ARM64_RELOC_UNSIGNED, 0) => RelocationKind::Absolute, + _ => RelocationKind::MachO { + value: reloc.r_type(), + relative: reloc.is_pic(), + }, + }, + mach::cputype::CPU_TYPE_X86 => match (reloc.r_type(), reloc.r_pcrel()) { + (mach::relocation::GENERIC_RELOC_VANILLA, 0) => RelocationKind::Absolute, + _ => RelocationKind::MachO { + value: reloc.r_type(), + relative: reloc.is_pic(), + }, + }, + mach::cputype::CPU_TYPE_X86_64 => match (reloc.r_type(), reloc.r_pcrel()) { + (mach::relocation::X86_64_RELOC_UNSIGNED, 0) => RelocationKind::Absolute, + (mach::relocation::X86_64_RELOC_SIGNED, 1) => { + encoding = RelocationEncoding::X86RipRelative; + RelocationKind::Relative + } + (mach::relocation::X86_64_RELOC_BRANCH, 1) => { + encoding = RelocationEncoding::X86Branch; + RelocationKind::Relative + } + (mach::relocation::X86_64_RELOC_GOT, 1) => RelocationKind::GotRelative, + (mach::relocation::X86_64_RELOC_GOT_LOAD, 1) => { + encoding = RelocationEncoding::X86RipRelativeMovq; + RelocationKind::GotRelative + } + _ => RelocationKind::MachO { + value: reloc.r_type(), + relative: reloc.is_pic(), + }, + }, + _ => RelocationKind::MachO { + value: reloc.r_type(), + relative: reloc.is_pic(), + }, + }; + let size = 8 << reloc.r_length(); + let target = if reloc.is_extern() { + RelocationTarget::Symbol(SymbolIndex(reloc.r_symbolnum())) + } else { + RelocationTarget::Section(SectionIndex(reloc.r_symbolnum())) + }; + let addend = if reloc.r_pcrel() != 0 { -4 } else { 0 }; + ( + reloc.r_address as u64, + Relocation { + kind, + encoding, + size, + target, + addend, + implicit_addend: true, + }, + ) + }) + } +} + +impl<'data, 'file> fmt::Debug for MachORelocationIterator<'data, 'file> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("MachORelocationIterator").finish() + } +} 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..c120db9a83 --- /dev/null +++ b/third_party/rust/object/src/read/mod.rs @@ -0,0 +1,250 @@ +//! Interface for reading object files. + +use crate::alloc::vec::Vec; +use crate::common::{RelocationEncoding, RelocationKind, SectionKind, SymbolKind, SymbolScope}; + +mod any; +pub use any::*; + +mod coff; +pub use coff::*; + +mod elf; +pub use elf::*; + +mod macho; +pub use macho::*; + +mod pe; +pub use pe::*; + +mod traits; +pub use traits::*; + +#[cfg(feature = "wasm")] +mod wasm; +#[cfg(feature = "wasm")] +pub use wasm::*; + +/// The native object file for the target platform. +#[cfg(target_os = "linux")] +pub type NativeFile<'data> = ElfFile<'data>; + +/// The native object file for the target platform. +#[cfg(target_os = "macos")] +pub type NativeFile<'data> = MachOFile<'data>; + +/// The native object file for the target platform. +#[cfg(target_os = "windows")] +pub type NativeFile<'data> = PeFile<'data>; + +/// The native object file for the target platform. +#[cfg(all(feature = "wasm", target_arch = "wasm32"))] +pub type NativeFile<'data> = WasmFile<'data>; + +/// The 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); + +/// A symbol table entry. +#[derive(Debug)] +pub struct Symbol<'data> { + name: Option<&'data str>, + address: u64, + size: u64, + kind: SymbolKind, + section_index: Option<SectionIndex>, + undefined: bool, + weak: bool, + scope: SymbolScope, +} + +impl<'data> Symbol<'data> { + /// Return the kind of this symbol. + #[inline] + pub fn kind(&self) -> SymbolKind { + self.kind + } + + /// Returns the section index for the section containing this symbol. + /// + /// May return `None` if the section is unknown or the symbol is undefined. + #[inline] + pub fn section_index(&self) -> Option<SectionIndex> { + self.section_index + } + + /// Return true if the symbol is undefined. + #[inline] + pub fn is_undefined(&self) -> bool { + self.undefined + } + + /// Return true if the symbol is weak. + #[inline] + pub fn is_weak(&self) -> bool { + self.weak + } + + /// Return true if the symbol visible outside of the compilation unit. + /// + /// This treats `SymbolScope::Unknown` as global. + #[inline] + pub fn is_global(&self) -> bool { + !self.is_local() + } + + /// Return true if the symbol is only visible within the compilation unit. + #[inline] + pub fn is_local(&self) -> bool { + self.scope == SymbolScope::Compilation + } + + /// Returns the symbol scope. + #[inline] + pub fn scope(&self) -> SymbolScope { + self.scope + } + + /// The name of the symbol. + #[inline] + pub fn name(&self) -> Option<&'data str> { + self.name + } + + /// The address of the symbol. May be zero if the address is unknown. + #[inline] + pub fn address(&self) -> u64 { + self.address + } + + /// The size of the symbol. May be zero if the size is unknown. + #[inline] + pub fn size(&self) -> u64 { + self.size + } +} + +/// A map from addresses to symbols. +#[derive(Debug)] +pub struct SymbolMap<'data> { + symbols: Vec<Symbol<'data>>, +} + +impl<'data> SymbolMap<'data> { + /// Get the symbol containing the given address. + pub fn get(&self, address: u64) -> Option<&Symbol<'data>> { + self.symbols + .binary_search_by(|symbol| { + if address < symbol.address { + std::cmp::Ordering::Greater + } else if address < symbol.address + symbol.size { + std::cmp::Ordering::Equal + } else { + std::cmp::Ordering::Less + } + }) + .ok() + .and_then(|index| self.symbols.get(index)) + } + + /// Get all symbols in the map. + pub fn symbols(&self) -> &[Symbol<'data>] { + &self.symbols + } + + /// Return true for symbols that should be included in the map. + fn filter(symbol: &Symbol<'_>) -> bool { + match symbol.kind() { + SymbolKind::Unknown | SymbolKind::Text | SymbolKind::Data => {} + SymbolKind::Null + | SymbolKind::Section + | SymbolKind::File + | SymbolKind::Label + | SymbolKind::Common + | SymbolKind::Tls => { + return false; + } + } + !symbol.is_undefined() && symbol.size() > 0 + } +} + +/// The target referenced by a relocation. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum RelocationTarget { + /// The target is a symbol. + Symbol(SymbolIndex), + /// The target is a section. + Section(SectionIndex), +} + +/// 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. + pub fn addend(&self) -> i64 { + self.addend + } + + /// Set the addend to use in the relocation calculation. + pub fn set_addend(&mut self, addend: i64) { + self.addend = addend + } + + /// Returns true if there is an implicit addend stored in the data at the offset + /// to be relocated. + pub fn has_implicit_addend(&self) -> bool { + self.implicit_addend + } +} + +fn data_range(data: &[u8], data_address: u64, range_address: u64, size: u64) -> Option<&[u8]> { + if range_address >= data_address { + let start_offset = (range_address - data_address) as usize; + let end_offset = start_offset + size as usize; + if end_offset <= data.len() { + return Some(&data[start_offset..end_offset]); + } + } + None +} diff --git a/third_party/rust/object/src/read/pe.rs b/third_party/rust/object/src/read/pe.rs new file mode 100644 index 0000000000..49d3fe83ce --- /dev/null +++ b/third_party/rust/object/src/read/pe.rs @@ -0,0 +1,407 @@ +use crate::alloc::borrow::Cow; +use crate::alloc::vec::Vec; +use goblin::pe; +use std::{cmp, iter, slice}; +use target_lexicon::Architecture; + +use crate::read::{ + self, Object, ObjectSection, ObjectSegment, Relocation, SectionIndex, SectionKind, Symbol, + SymbolIndex, SymbolKind, SymbolMap, SymbolScope, +}; + +/// A PE object file. +#[derive(Debug)] +pub struct PeFile<'data> { + pe: pe::PE<'data>, + data: &'data [u8], +} + +impl<'data> PeFile<'data> { + /// Get the PE headers of the file. + // TODO: this is temporary to allow access to features this crate doesn't provide yet + #[inline] + pub fn pe(&self) -> &pe::PE<'data> { + &self.pe + } + + /// Parse the raw PE file data. + pub fn parse(data: &'data [u8]) -> Result<Self, &'static str> { + let pe = pe::PE::parse(data).map_err(|_| "Could not parse PE header")?; + Ok(PeFile { pe, data }) + } + + fn section_alignment(&self) -> u64 { + u64::from( + self.pe + .header + .optional_header + .map(|h| h.windows_fields.section_alignment) + .unwrap_or(0x1000), + ) + } +} + +impl<'data, 'file> Object<'data, 'file> for PeFile<'data> +where + 'data: 'file, +{ + type Segment = PeSegment<'data, 'file>; + type SegmentIterator = PeSegmentIterator<'data, 'file>; + type Section = PeSection<'data, 'file>; + type SectionIterator = PeSectionIterator<'data, 'file>; + type SymbolIterator = PeSymbolIterator<'data, 'file>; + + fn architecture(&self) -> Architecture { + match self.pe.header.coff_header.machine { + // TODO: Arm/Arm64 + pe::header::COFF_MACHINE_X86 => Architecture::I386, + pe::header::COFF_MACHINE_X86_64 => Architecture::X86_64, + _ => Architecture::Unknown, + } + } + + #[inline] + fn is_little_endian(&self) -> bool { + // TODO: always little endian? The COFF header has some bits in the + // characteristics flags, but these are obsolete. + true + } + + #[inline] + fn is_64(&self) -> bool { + self.pe.is_64 + } + + fn segments(&'file self) -> PeSegmentIterator<'data, 'file> { + PeSegmentIterator { + file: self, + iter: self.pe.sections.iter(), + } + } + + fn section_by_name(&'file self, section_name: &str) -> Option<PeSection<'data, 'file>> { + self.sections() + .find(|section| section.name() == Some(section_name)) + } + + fn section_by_index(&'file self, index: SectionIndex) -> Option<PeSection<'data, 'file>> { + self.sections().find(|section| section.index() == index) + } + + fn sections(&'file self) -> PeSectionIterator<'data, 'file> { + PeSectionIterator { + file: self, + iter: self.pe.sections.iter().enumerate(), + } + } + + fn symbol_by_index(&self, _index: SymbolIndex) -> Option<Symbol<'data>> { + // TODO: return COFF symbols for object files + None + } + + fn symbols(&'file self) -> PeSymbolIterator<'data, 'file> { + // TODO: return COFF symbols for object files + PeSymbolIterator { + index: 0, + exports: [].iter(), + imports: [].iter(), + } + } + + fn dynamic_symbols(&'file self) -> PeSymbolIterator<'data, 'file> { + PeSymbolIterator { + index: 0, + exports: self.pe.exports.iter(), + imports: self.pe.imports.iter(), + } + } + + fn symbol_map(&self) -> SymbolMap<'data> { + // TODO: untested + let mut symbols: Vec<_> = self + .symbols() + .map(|(_, s)| s) + .filter(SymbolMap::filter) + .collect(); + symbols.sort_by_key(|x| x.address); + SymbolMap { symbols } + } + + fn has_debug_symbols(&self) -> bool { + // TODO: check if CodeView-in-PE still works + for section in &self.pe.sections { + if let Ok(name) = section.name() { + if name == ".debug_info" { + return true; + } + } + } + false + } + + fn entry(&self) -> u64 { + self.pe.entry as u64 + } +} + +/// An iterator over the loadable sections of a `PeFile`. +#[derive(Debug)] +pub struct PeSegmentIterator<'data, 'file> +where + 'data: 'file, +{ + file: &'file PeFile<'data>, + iter: slice::Iter<'file, pe::section_table::SectionTable>, +} + +impl<'data, 'file> Iterator for PeSegmentIterator<'data, 'file> { + type Item = PeSegment<'data, 'file>; + + fn next(&mut self) -> Option<Self::Item> { + self.iter.next().map(|section| PeSegment { + file: self.file, + section, + }) + } +} + +/// A loadable section of a `PeFile`. +#[derive(Debug)] +pub struct PeSegment<'data, 'file> +where + 'data: 'file, +{ + file: &'file PeFile<'data>, + section: &'file pe::section_table::SectionTable, +} + +impl<'data, 'file> ObjectSegment<'data> for PeSegment<'data, 'file> { + #[inline] + fn address(&self) -> u64 { + u64::from(self.section.virtual_address) + } + + #[inline] + fn size(&self) -> u64 { + u64::from(self.section.virtual_size) + } + + #[inline] + fn align(&self) -> u64 { + self.file.section_alignment() + } + + #[inline] + fn file_range(&self) -> (u64, u64) { + ( + self.section.pointer_to_raw_data as u64, + self.section.size_of_raw_data as u64, + ) + } + + fn data(&self) -> &'data [u8] { + let offset = self.section.pointer_to_raw_data as usize; + let size = cmp::min(self.section.virtual_size, self.section.size_of_raw_data) as usize; + &self.file.data[offset..][..size] + } + + fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]> { + read::data_range(self.data(), self.address(), address, size) + } + + #[inline] + fn name(&self) -> Option<&str> { + self.section.name().ok() + } +} + +/// An iterator over the sections of a `PeFile`. +#[derive(Debug)] +pub struct PeSectionIterator<'data, 'file> +where + 'data: 'file, +{ + file: &'file PeFile<'data>, + iter: iter::Enumerate<slice::Iter<'file, pe::section_table::SectionTable>>, +} + +impl<'data, 'file> Iterator for PeSectionIterator<'data, 'file> { + type Item = PeSection<'data, 'file>; + + fn next(&mut self) -> Option<Self::Item> { + self.iter.next().map(|(index, section)| PeSection { + file: self.file, + index: SectionIndex(index), + section, + }) + } +} + +/// A section of a `PeFile`. +#[derive(Debug)] +pub struct PeSection<'data, 'file> +where + 'data: 'file, +{ + file: &'file PeFile<'data>, + index: SectionIndex, + section: &'file pe::section_table::SectionTable, +} + +impl<'data, 'file> PeSection<'data, 'file> { + fn raw_data(&self) -> &'data [u8] { + let offset = self.section.pointer_to_raw_data as usize; + let size = cmp::min(self.section.virtual_size, self.section.size_of_raw_data) as usize; + &self.file.data[offset..][..size] + } +} + +impl<'data, 'file> ObjectSection<'data> for PeSection<'data, 'file> { + type RelocationIterator = PeRelocationIterator; + + #[inline] + fn index(&self) -> SectionIndex { + self.index + } + + #[inline] + fn address(&self) -> u64 { + u64::from(self.section.virtual_address) + } + + #[inline] + fn size(&self) -> u64 { + u64::from(self.section.virtual_size) + } + + #[inline] + fn align(&self) -> u64 { + self.file.section_alignment() + } + + #[inline] + fn file_range(&self) -> Option<(u64, u64)> { + Some(( + self.section.pointer_to_raw_data as u64, + self.section.size_of_raw_data as u64, + )) + } + + fn data(&self) -> Cow<'data, [u8]> { + Cow::from(self.raw_data()) + } + + fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]> { + read::data_range(self.raw_data(), self.address(), address, size) + } + + #[inline] + fn uncompressed_data(&self) -> Cow<'data, [u8]> { + // TODO: does PE support compression? + self.data() + } + + fn name(&self) -> Option<&str> { + self.section.name().ok() + } + + #[inline] + fn segment_name(&self) -> Option<&str> { + None + } + + #[inline] + fn kind(&self) -> SectionKind { + if self.section.characteristics + & (pe::section_table::IMAGE_SCN_CNT_CODE | pe::section_table::IMAGE_SCN_MEM_EXECUTE) + != 0 + { + SectionKind::Text + } else if self.section.characteristics & pe::section_table::IMAGE_SCN_CNT_INITIALIZED_DATA + != 0 + { + SectionKind::Data + } else if self.section.characteristics & pe::section_table::IMAGE_SCN_CNT_UNINITIALIZED_DATA + != 0 + { + SectionKind::UninitializedData + } else { + SectionKind::Unknown + } + } + + fn relocations(&self) -> PeRelocationIterator { + PeRelocationIterator + } +} + +/// An iterator over the symbols of a `PeFile`. +#[derive(Debug)] +pub struct PeSymbolIterator<'data, 'file> +where + 'data: 'file, +{ + index: usize, + exports: slice::Iter<'file, pe::export::Export<'data>>, + imports: slice::Iter<'file, pe::import::Import<'data>>, +} + +impl<'data, 'file> Iterator for PeSymbolIterator<'data, 'file> { + type Item = (SymbolIndex, Symbol<'data>); + + fn next(&mut self) -> Option<Self::Item> { + if let Some(export) = self.exports.next() { + let index = SymbolIndex(self.index); + self.index += 1; + return Some(( + index, + Symbol { + name: export.name, + address: export.rva as u64, + size: 0, + kind: SymbolKind::Unknown, + // TODO: can we find a section? + section_index: None, + undefined: false, + weak: false, + scope: SymbolScope::Dynamic, + }, + )); + } + if let Some(import) = self.imports.next() { + let index = SymbolIndex(self.index); + self.index += 1; + let name = match import.name { + Cow::Borrowed(name) => Some(name), + _ => None, + }; + return Some(( + index, + Symbol { + name, + address: 0, + size: 0, + kind: SymbolKind::Unknown, + section_index: None, + undefined: true, + weak: false, + scope: SymbolScope::Dynamic, + }, + )); + } + None + } +} + +/// An iterator over the relocations in an `PeSection`. +#[derive(Debug)] +pub struct PeRelocationIterator; + +impl Iterator for PeRelocationIterator { + type Item = (u64, Relocation); + + fn next(&mut self) -> Option<Self::Item> { + None + } +} 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..70b11e798a --- /dev/null +++ b/third_party/rust/object/src/read/traits.rs @@ -0,0 +1,220 @@ +use crate::alloc::borrow::Cow; +use crate::{Relocation, SectionIndex, SectionKind, Symbol, SymbolIndex, SymbolMap}; +use target_lexicon::{Architecture, Endianness}; +use uuid::Uuid; + +/// An object file. +pub trait Object<'data, 'file> { + /// A segment in the object file. + type Segment: ObjectSegment<'data>; + + /// An iterator over the segments in the object file. + type SegmentIterator: Iterator<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>; + + /// An iterator over the symbols in the object file. + type SymbolIterator: Iterator<Item = (SymbolIndex, Symbol<'data>)>; + + /// 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; + + /// Get an iterator over the segments in the file. + fn segments(&'file self) -> Self::SegmentIterator; + + /// Get the entry point address of the binary + fn entry(&'file self) -> u64; + + /// Get the section named `section_name`, if such a section exists. + /// + /// If `section_name` starts with a '.' then it is treated as a system section name, + /// and is compared using the conventions specific to the object file format. This + /// includes: + /// - if ".text" is requested for a Mach-O object file, then the actual + /// section name that is searched for is "__text". + /// - if ".debug_info" is requested for an ELF object file, then + /// ".zdebug_info" may be returned (and similarly for other debug sections). + /// + /// For some object files, multiple segments may contain sections with the same + /// name. In this case, the first matching section will be used. + fn section_by_name(&'file self, section_name: &str) -> Option<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. + fn section_by_index(&'file self, index: SectionIndex) -> Option<Self::Section>; + + /// Get the contents of the section named `section_name`, if such + /// a section exists. + /// + /// The `section_name` is interpreted according to `Self::section_by_name`. + /// + /// This may decompress section data. + fn section_data_by_name(&'file self, section_name: &str) -> Option<Cow<'data, [u8]>> { + self.section_by_name(section_name) + .map(|section| section.uncompressed_data()) + } + + /// Get an iterator over the sections in the file. + fn sections(&'file self) -> Self::SectionIterator; + + /// Get the debugging symbol at the given index. + /// + /// This is similar to `self.symbols().nth(index)`, except that + /// the index will take into account malformed or unsupported symbols. + fn symbol_by_index(&self, index: SymbolIndex) -> Option<Symbol<'data>>; + + /// Get an iterator over the debugging symbols in the file. + /// + /// This may skip over symbols that are malformed or unsupported. + fn symbols(&'file self) -> Self::SymbolIterator; + + /// Get the data for the given symbol. + fn symbol_data(&'file self, symbol: &Symbol<'data>) -> Option<&'data [u8]> { + if symbol.is_undefined() { + return None; + } + let address = symbol.address(); + let size = symbol.size(); + if let Some(index) = symbol.section_index() { + self.section_by_index(index) + .and_then(|section| section.data_range(address, size)) + } else { + self.segments() + .find_map(|segment| segment.data_range(address, size)) + } + } + + /// Get an iterator over the dynamic linking symbols in the file. + /// + /// This may skip over symbols that are malformed or unsupported. + fn dynamic_symbols(&'file self) -> Self::SymbolIterator; + + /// Construct a map from addresses to symbols. + fn symbol_map(&self) -> SymbolMap<'data>; + + /// Return true if the file contains debug information sections, false if not. + fn has_debug_symbols(&self) -> bool; + + /// The UUID from a Mach-O `LC_UUID` load command. + #[inline] + fn mach_uuid(&self) -> Option<Uuid> { + None + } + + /// The build ID from an ELF `NT_GNU_BUILD_ID` note. + #[inline] + fn build_id(&self) -> Option<&'data [u8]> { + None + } + + /// The filename and CRC from a `.gnu_debuglink` section. + #[inline] + fn gnu_debuglink(&self) -> Option<(&'data [u8], u32)> { + None + } +} + +/// A loadable segment defined in an object file. +/// +/// For ELF, this is a program header with type `PT_LOAD`. +/// For Mach-O, this is a load command with type `LC_SEGMENT` or `LC_SEGMENT_64`. +pub trait ObjectSegment<'data> { + /// Returns the virtual address of the segment. + fn address(&self) -> u64; + + /// Returns the size of the segment in memory. + fn size(&self) -> u64; + + /// Returns the alignment of the segment in memory. + fn align(&self) -> u64; + + /// Returns 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) -> &'data [u8]; + + /// Return the segment data in the given range. + fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]>; + + /// Returns the name of the segment. + fn name(&self) -> Option<&str>; +} + +/// A section defined in an object file. +pub trait ObjectSection<'data> { + /// An iterator over the relocations for a section. + /// + /// The first field in the item tuple is the section offset + /// that the relocation applies to. + type RelocationIterator: Iterator<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) -> Cow<'data, [u8]>; + + /// Return the raw contents of the section data in the given range. + /// + /// This does not do any decompression. + fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]>; + + /// Returns the uncompressed contents of the section. + /// The length of this data may be different from the size of the + /// section in memory. + fn uncompressed_data(&self) -> Cow<'data, [u8]>; + + /// Returns the name of the section. + fn name(&self) -> Option<&str>; + + /// Returns the name of the segment for this section. + fn segment_name(&self) -> Option<&str>; + + /// Return the kind of this section. + fn kind(&self) -> SectionKind; + + /// Get the relocations for this section. + fn relocations(&self) -> Self::RelocationIterator; +} 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..e13343e5b1 --- /dev/null +++ b/third_party/rust/object/src/read/wasm.rs @@ -0,0 +1,315 @@ +use crate::alloc::vec::Vec; +use parity_wasm::elements::{self, Deserialize}; +use std::borrow::Cow; +use std::{iter, slice}; +use target_lexicon::Architecture; + +use crate::read::{ + Object, ObjectSection, ObjectSegment, Relocation, SectionIndex, SectionKind, Symbol, + SymbolIndex, SymbolMap, +}; + +/// A WebAssembly object file. +#[derive(Debug)] +pub struct WasmFile { + module: elements::Module, +} + +impl<'data> WasmFile { + /// Parse the raw wasm data. + pub fn parse(mut data: &'data [u8]) -> Result<Self, &'static str> { + let module = + elements::Module::deserialize(&mut data).map_err(|_| "failed to parse wasm")?; + Ok(WasmFile { module }) + } +} + +fn serialize_to_cow<'a, S>(s: S) -> Option<Cow<'a, [u8]>> +where + S: elements::Serialize, +{ + let mut buf = Vec::new(); + s.serialize(&mut buf).ok()?; + Some(Cow::from(buf)) +} + +impl<'file> Object<'static, 'file> for WasmFile { + type Segment = WasmSegment<'file>; + type SegmentIterator = WasmSegmentIterator<'file>; + type Section = WasmSection<'file>; + type SectionIterator = WasmSectionIterator<'file>; + type SymbolIterator = WasmSymbolIterator<'file>; + + #[inline] + fn architecture(&self) -> Architecture { + Architecture::Wasm32 + } + + #[inline] + fn is_little_endian(&self) -> bool { + true + } + + #[inline] + fn is_64(&self) -> bool { + false + } + + fn segments(&'file self) -> Self::SegmentIterator { + WasmSegmentIterator { file: self } + } + + fn entry(&'file self) -> u64 { + self.module + .start_section() + .map_or(u64::max_value(), u64::from) + } + + fn section_by_name(&'file self, section_name: &str) -> Option<WasmSection<'file>> { + self.sections() + .find(|section| section.name() == Some(section_name)) + } + + fn section_by_index(&'file self, index: SectionIndex) -> Option<WasmSection<'file>> { + self.sections().find(|section| section.index() == index) + } + + fn sections(&'file self) -> Self::SectionIterator { + WasmSectionIterator { + sections: self.module.sections().iter().enumerate(), + } + } + + fn symbol_by_index(&self, _index: SymbolIndex) -> Option<Symbol<'static>> { + unimplemented!() + } + + fn symbols(&'file self) -> Self::SymbolIterator { + WasmSymbolIterator { file: self } + } + + fn dynamic_symbols(&'file self) -> Self::SymbolIterator { + WasmSymbolIterator { file: self } + } + + fn symbol_map(&self) -> SymbolMap<'static> { + SymbolMap { + symbols: Vec::new(), + } + } + + fn has_debug_symbols(&self) -> bool { + // We ignore the "name" section, and use this to mean whether the wasm + // has DWARF. + self.module.sections().iter().any(|s| match *s { + elements::Section::Custom(ref c) => c.name().starts_with(".debug_"), + _ => false, + }) + } +} + +/// An iterator over the segments of an `WasmFile`. +#[derive(Debug)] +pub struct WasmSegmentIterator<'file> { + file: &'file WasmFile, +} + +impl<'file> Iterator for WasmSegmentIterator<'file> { + type Item = WasmSegment<'file>; + + fn next(&mut self) -> Option<Self::Item> { + None + } +} + +/// A segment of an `WasmFile`. +#[derive(Debug)] +pub struct WasmSegment<'file> { + file: &'file WasmFile, +} + +impl<'file> ObjectSegment<'static> for WasmSegment<'file> { + #[inline] + fn address(&self) -> u64 { + unreachable!() + } + + #[inline] + fn size(&self) -> u64 { + unreachable!() + } + + #[inline] + fn align(&self) -> u64 { + unreachable!() + } + + #[inline] + fn file_range(&self) -> (u64, u64) { + unreachable!() + } + + fn data(&self) -> &'static [u8] { + unreachable!() + } + + fn data_range(&self, _address: u64, _size: u64) -> Option<&'static [u8]> { + unreachable!() + } + + #[inline] + fn name(&self) -> Option<&str> { + unreachable!() + } +} + +/// An iterator over the sections of an `WasmFile`. +#[derive(Debug)] +pub struct WasmSectionIterator<'file> { + sections: iter::Enumerate<slice::Iter<'file, elements::Section>>, +} + +impl<'file> Iterator for WasmSectionIterator<'file> { + type Item = WasmSection<'file>; + + fn next(&mut self) -> Option<Self::Item> { + self.sections.next().map(|(index, section)| WasmSection { + index: SectionIndex(index), + section, + }) + } +} + +/// A section of an `WasmFile`. +#[derive(Debug)] +pub struct WasmSection<'file> { + index: SectionIndex, + section: &'file elements::Section, +} + +impl<'file> ObjectSection<'static> for WasmSection<'file> { + type RelocationIterator = WasmRelocationIterator; + + #[inline] + fn index(&self) -> SectionIndex { + self.index + } + + #[inline] + fn address(&self) -> u64 { + 1 + } + + #[inline] + fn size(&self) -> u64 { + serialize_to_cow(self.section.clone()).map_or(0, |b| b.len() as u64) + } + + #[inline] + fn align(&self) -> u64 { + 1 + } + + #[inline] + fn file_range(&self) -> Option<(u64, u64)> { + None + } + + fn data(&self) -> Cow<'static, [u8]> { + match *self.section { + elements::Section::Custom(ref section) => Some(section.payload().to_vec().into()), + elements::Section::Start(section) => { + serialize_to_cow(elements::VarUint32::from(section)) + } + _ => serialize_to_cow(self.section.clone()), + } + .unwrap_or_else(|| Cow::from(&[][..])) + } + + fn data_range(&self, _address: u64, _size: u64) -> Option<&'static [u8]> { + unimplemented!() + } + + #[inline] + fn uncompressed_data(&self) -> Cow<'static, [u8]> { + // TODO: does wasm support compression? + self.data() + } + + fn name(&self) -> Option<&str> { + match *self.section { + elements::Section::Unparsed { .. } => None, + elements::Section::Custom(ref c) => Some(c.name()), + elements::Section::Type(_) => Some("Type"), + elements::Section::Import(_) => Some("Import"), + elements::Section::Function(_) => Some("Function"), + elements::Section::Table(_) => Some("Table"), + elements::Section::Memory(_) => Some("Memory"), + elements::Section::Global(_) => Some("Global"), + elements::Section::Export(_) => Some("Export"), + elements::Section::Start(_) => Some("Start"), + elements::Section::Element(_) => Some("Element"), + elements::Section::Code(_) => Some("Code"), + elements::Section::DataCount(_) => Some("DataCount"), + elements::Section::Data(_) => Some("Data"), + elements::Section::Name(_) => Some("Name"), + elements::Section::Reloc(_) => Some("Reloc"), + } + } + + #[inline] + fn segment_name(&self) -> Option<&str> { + None + } + + fn kind(&self) -> SectionKind { + match *self.section { + elements::Section::Unparsed { .. } => SectionKind::Unknown, + elements::Section::Custom(_) => SectionKind::Unknown, + elements::Section::Type(_) => SectionKind::Other, + elements::Section::Import(_) => SectionKind::Other, + elements::Section::Function(_) => SectionKind::Other, + elements::Section::Table(_) => SectionKind::Other, + elements::Section::Memory(_) => SectionKind::Other, + elements::Section::Global(_) => SectionKind::Other, + elements::Section::Export(_) => SectionKind::Other, + elements::Section::Start(_) => SectionKind::Other, + elements::Section::Element(_) => SectionKind::Other, + elements::Section::Code(_) => SectionKind::Text, + elements::Section::DataCount(_) => SectionKind::Other, + elements::Section::Data(_) => SectionKind::Data, + elements::Section::Name(_) => SectionKind::Other, + elements::Section::Reloc(_) => SectionKind::Other, + } + } + + fn relocations(&self) -> WasmRelocationIterator { + WasmRelocationIterator + } +} + +/// An iterator over the symbols of an `WasmFile`. +#[derive(Debug)] +pub struct WasmSymbolIterator<'file> { + file: &'file WasmFile, +} + +impl<'file> Iterator for WasmSymbolIterator<'file> { + type Item = (SymbolIndex, Symbol<'static>); + + fn next(&mut self) -> Option<Self::Item> { + unimplemented!() + } +} + +/// An iterator over the relocations in an `WasmSection`. +#[derive(Debug)] +pub struct WasmRelocationIterator; + +impl Iterator for WasmRelocationIterator { + type Item = (u64, Relocation); + + fn next(&mut self) -> Option<Self::Item> { + 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..5077bc7f0a --- /dev/null +++ b/third_party/rust/object/src/write/coff.rs @@ -0,0 +1,486 @@ +use crc32fast; +use scroll::ctx::SizeWith; +use scroll::IOwrite; +use std::iter; +use std::string::String; + +use crate::alloc::vec::Vec; +use crate::write::string::*; +use crate::write::util::*; +use crate::write::*; + +mod coff { + pub use goblin::pe::characteristic::*; + pub use goblin::pe::header::*; + pub use goblin::pe::relocation::*; + pub use goblin::pe::section_table::*; + pub use goblin::pe::symbol::*; +} + +#[derive(Default, Clone, Copy)] +struct SectionOffsets { + offset: usize, + str_id: Option<StringId>, + reloc_offset: usize, +} + +#[derive(Default, Clone, Copy)] +struct SymbolOffsets { + index: usize, + str_id: Option<StringId>, + aux_count: u8, +} + +impl Object { + pub(crate) fn coff_section_info( + &self, + section: StandardSection, + ) -> (&'static [u8], &'static [u8], SectionKind) { + match section { + StandardSection::Text => (&[], &b".text"[..], SectionKind::Text), + StandardSection::Data => (&[], &b".data"[..], SectionKind::Data), + StandardSection::ReadOnlyData + | StandardSection::ReadOnlyDataWithRel + | StandardSection::ReadOnlyString => (&[], &b".rdata"[..], SectionKind::ReadOnlyData), + StandardSection::UninitializedData => { + (&[], &b".bss"[..], SectionKind::UninitializedData) + } + StandardSection::Tls => (&[], &b".tls$"[..], SectionKind::Tls), + StandardSection::UninitializedTls => { + // Unsupported section. + (&[], &[], SectionKind::UninitializedTls) + } + StandardSection::TlsVariables => { + // Unsupported section. + (&[], &[], SectionKind::TlsVariables) + } + } + } + + pub(crate) fn coff_subsection_name(&self, section: &[u8], value: &[u8]) -> Vec<u8> { + let mut name = section.to_vec(); + name.push(b'$'); + name.extend(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 => match relocation.kind { + RelocationKind::Relative => { + // IMAGE_REL_I386_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.pointer_width().unwrap().bytes(); + + let mut name = b".rdata$.refptr.".to_vec(); + name.extend(&self.symbols[symbol_id.0].name); + 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(&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: Some(section_id), + }); + self.stub_symbols.insert(symbol_id, stub_id); + + stub_id + } + + pub(crate) fn coff_write(&self) -> Result<Vec<u8>, String> { + // Calculate offsets of everything, and build strtab. + let mut offset = 0; + let mut strtab = StringTable::default(); + + // COFF header. + let ctx = scroll::LE; + offset += coff::CoffHeader::size_with(&ctx); + + // Section headers. + offset += self.sections.len() * coff::SectionTable::size_with(&ctx); + + // 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 = offset; + } + + // Calculate size of relocations. + let count = section.relocations.len(); + if count != 0 { + section_offsets[index].reloc_offset = offset; + offset += count * coff::Relocation::size_with(&ctx); + } + } + + // 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 auxilary symbol records. + let aux_count = + (symbol.name.len() + coff::COFF_SYMBOL_SIZE - 1) / coff::COFF_SYMBOL_SIZE; + 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::COFF_SYMBOL_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. + let mut buffer = Vec::with_capacity(offset); + + // Write file header. + let header = coff::CoffHeader { + machine: match self.architecture { + Architecture::I386 => coff::COFF_MACHINE_X86, + Architecture::X86_64 => coff::COFF_MACHINE_X86_64, + _ => { + return Err(format!( + "unimplemented architecture {:?}", + self.architecture + )) + } + }, + number_of_sections: self.sections.len() as u16, + time_date_stamp: 0, + pointer_to_symbol_table: symtab_offset as u32, + number_of_symbol_table: symtab_count as u32, + size_of_optional_header: 0, + characteristics: 0, + }; + buffer.iowrite_with(header, ctx).unwrap(); + + // Write section headers. + for (index, section) in self.sections.iter().enumerate() { + // TODO: IMAGE_SCN_LNK_COMDAT + let characteristics = 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::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::Tls + | SectionKind::UninitializedTls + | SectionKind::TlsVariables + | SectionKind::Unknown + | SectionKind::Metadata => { + return Err(format!("unimplemented section {:?}", section.kind)) + } + }; + let align = 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(format!("unimplemented section align {}", section.align)), + }; + let mut coff_section = coff::SectionTable { + name: [0; 8], + real_name: None, + virtual_size: if section.data.is_empty() { + section.size as u32 + } else { + 0 + }, + virtual_address: 0, + size_of_raw_data: section.data.len() as u32, + pointer_to_raw_data: if section.data.is_empty() { + 0 + } else { + section_offsets[index].offset as u32 + }, + pointer_to_relocations: section_offsets[index].reloc_offset as u32, + pointer_to_linenumbers: 0, + number_of_relocations: section.relocations.len() as u16, + number_of_linenumbers: 0, + characteristics: characteristics | align, + }; + if section.name.len() <= 8 { + coff_section.name[..section.name.len()].copy_from_slice(§ion.name); + } else { + let str_offset = strtab.get_offset(section_offsets[index].str_id.unwrap()); + coff_section.set_name_offset(str_offset).unwrap(); + } + buffer.iowrite_with(coff_section, ctx).unwrap(); + } + + // Write section data and relocations. + for (index, section) in self.sections.iter().enumerate() { + let len = section.data.len(); + if len != 0 { + write_align(&mut buffer, 4); + debug_assert_eq!(section_offsets[index].offset, buffer.len()); + buffer.extend(§ion.data); + } + + if !section.relocations.is_empty() { + debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len()); + 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(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::SectionOffset, 32, 0) => coff::IMAGE_REL_AMD64_SECREL, + (RelocationKind::SectionOffset, 7, 0) => coff::IMAGE_REL_AMD64_SECREL7, + (RelocationKind::Coff(x), _, _) => x, + _ => return Err(format!("unimplemented relocation {:?}", reloc)), + }, + _ => { + return Err(format!( + "unimplemented architecture {:?}", + self.architecture + )) + } + }; + buffer + .iowrite_with( + coff::Relocation { + virtual_address: reloc.offset as u32, + symbol_table_index: symbol_offsets[reloc.symbol.0].index as u32, + typ, + }, + ctx, + ) + .unwrap(); + } + } + } + + // Write symbols. + debug_assert_eq!(symtab_offset, buffer.len()); + for (index, symbol) in self.symbols.iter().enumerate() { + let mut name = &symbol.name[..]; + let mut section_number = symbol.section.map(|x| x.0 + 1).unwrap_or(0) as i16; + 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 auxilary symbol records. + name = b".file"; + section_number = coff::IMAGE_SYM_DEBUG; + coff::IMAGE_SYM_CLASS_FILE + } + SymbolKind::Section => coff::IMAGE_SYM_CLASS_STATIC, + SymbolKind::Label => coff::IMAGE_SYM_CLASS_LABEL, + SymbolKind::Text | SymbolKind::Data => { + match symbol.scope { + _ if symbol.is_undefined() => coff::IMAGE_SYM_CLASS_EXTERNAL, + // TODO: does this need aux symbol records too? + _ if symbol.weak => coff::IMAGE_SYM_CLASS_WEAK_EXTERNAL, + SymbolScope::Unknown => { + return Err(format!("unimplemented symbol scope {:?}", symbol)) + } + SymbolScope::Compilation => coff::IMAGE_SYM_CLASS_STATIC, + SymbolScope::Linkage | SymbolScope::Dynamic => { + coff::IMAGE_SYM_CLASS_EXTERNAL + } + } + } + _ => return Err(format!("unimplemented symbol {:?}", symbol.kind)), + }; + let number_of_aux_symbols = symbol_offsets[index].aux_count; + let mut coff_symbol = coff::Symbol { + name: [0; 8], + value: symbol.value as u32, + section_number, + 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.set_name_offset(str_offset as u32); + } + buffer.iowrite_with(coff_symbol, ctx).unwrap(); + + match symbol.kind { + SymbolKind::File => { + let aux_len = number_of_aux_symbols as usize * coff::COFF_SYMBOL_SIZE; + debug_assert!(aux_len >= symbol.name.len()); + buffer.extend(&symbol.name); + buffer.extend(iter::repeat(0).take(aux_len - symbol.name.len())); + } + SymbolKind::Section => { + debug_assert_eq!(number_of_aux_symbols, 1); + let section = &self.sections[symbol.section.unwrap().0]; + buffer + .iowrite_with( + coff::AuxSectionDefinition { + length: section.data.len() as u32, + number_of_relocations: section.relocations.len() as u16, + number_of_line_numbers: 0, + checksum: checksum(§ion.data), + number: section_number as u16, + // TODO: COMDAT + selection: 0, + unused: [0; 3], + }, + ctx, + ) + .unwrap(); + } + _ => { + debug_assert_eq!(number_of_aux_symbols, 0); + } + } + } + + // Write strtab section. + debug_assert_eq!(strtab_offset, buffer.len()); + buffer.iowrite_with(strtab_len as u32, ctx).unwrap(); + buffer.extend(&strtab_data); + + Ok(buffer) + } +} + +// 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.rs b/third_party/rust/object/src/write/elf.rs new file mode 100644 index 0000000000..e784794dc8 --- /dev/null +++ b/third_party/rust/object/src/write/elf.rs @@ -0,0 +1,736 @@ +use scroll::ctx::SizeWith; +use scroll::IOwrite; +use std::string::String; + +use crate::alloc::vec::Vec; +use crate::write::string::*; +use crate::write::util::*; +use crate::write::*; + +mod elf { + pub use goblin::elf::header::*; + pub use goblin::elf::program_header::*; + pub use goblin::elf::reloc::*; + pub use goblin::elf::section_header::*; + pub use goblin::elf::sym::*; +} + +#[derive(Default, Clone, Copy)] +struct SectionOffsets { + index: usize, + offset: usize, + str_id: Option<StringId>, + reloc_index: usize, + reloc_offset: usize, + reloc_len: usize, + reloc_str_id: Option<StringId>, +} + +#[derive(Default, Clone, Copy)] +struct SymbolOffsets { + index: usize, + str_id: Option<StringId>, +} + +impl Object { + pub(crate) fn elf_section_info( + &self, + section: StandardSection, + ) -> (&'static [u8], &'static [u8], SectionKind) { + match section { + StandardSection::Text => (&[], &b".text"[..], SectionKind::Text), + StandardSection::Data => (&[], &b".data"[..], SectionKind::Data), + StandardSection::ReadOnlyData + | StandardSection::ReadOnlyDataWithRel + | StandardSection::ReadOnlyString => (&[], &b".rodata"[..], SectionKind::ReadOnlyData), + StandardSection::UninitializedData => { + (&[], &b".bss"[..], SectionKind::UninitializedData) + } + StandardSection::Tls => (&[], &b".tdata"[..], SectionKind::Tls), + StandardSection::UninitializedTls => { + (&[], &b".tbss"[..], SectionKind::UninitializedTls) + } + StandardSection::TlsVariables => { + // Unsupported section. + (&[], &[], SectionKind::TlsVariables) + } + } + } + + pub(crate) fn elf_subsection_name(&self, section: &[u8], value: &[u8]) -> Vec<u8> { + let mut name = section.to_vec(); + name.push(b'.'); + name.extend(value); + name + } + + fn elf_has_relocation_addend(&self) -> Result<bool, String> { + Ok(match self.architecture { + Architecture::Arm(_) => false, + Architecture::Aarch64(_) => false, + Architecture::I386 => false, + Architecture::X86_64 => true, + _ => { + return Err(format!( + "unimplemented architecture {:?}", + self.architecture + )) + } + }) + } + + pub(crate) fn elf_fixup_relocation( + &mut self, + mut relocation: &mut Relocation, + ) -> Result<i64, String> { + // 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 { + 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_write(&self) -> Result<Vec<u8>, String> { + let (container, pointer_align) = match self.architecture.pointer_width().unwrap() { + PointerWidth::U16 | PointerWidth::U32 => (goblin::container::Container::Little, 4), + PointerWidth::U64 => (goblin::container::Container::Big, 8), + }; + let endian = match self.architecture.endianness().unwrap() { + Endianness::Little => goblin::container::Endian::Little, + Endianness::Big => goblin::container::Endian::Big, + }; + let ctx = goblin::container::Ctx::new(container, endian); + let is_rela = self.elf_has_relocation_addend()?; + let reloc_ctx = (is_rela, ctx); + + // Calculate offsets of everything. + let mut offset = 0; + + // ELF header. + let e_ehsize = elf::Header::size_with(&ctx); + offset += e_ehsize; + + // Create reloc section header names. + let reloc_names: Vec<_> = self + .sections + .iter() + .map(|section| { + let mut reloc_name = Vec::new(); + 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(); + + // Calculate size of section data. + let mut shstrtab = StringTable::default(); + let mut section_offsets = vec![SectionOffsets::default(); self.sections.len()]; + // Null section. + let mut e_shnum = 1; + for (index, section) in self.sections.iter().enumerate() { + section_offsets[index].str_id = Some(shstrtab.add(§ion.name)); + section_offsets[index].index = e_shnum; + e_shnum += 1; + + let len = section.data.len(); + if len != 0 { + offset = align(offset, section.align as usize); + section_offsets[index].offset = offset; + offset += len; + } else { + section_offsets[index].offset = offset; + } + + if !section.relocations.is_empty() { + section_offsets[index].reloc_str_id = Some(shstrtab.add(&reloc_names[index])); + section_offsets[index].reloc_index = e_shnum; + e_shnum += 1; + } + } + + // Calculate index of symbols and add symbol strings to strtab. + let mut strtab = StringTable::default(); + let mut symbol_offsets = vec![SymbolOffsets::default(); self.symbols.len()]; + // Null symbol. + let mut symtab_count = 1; + // Local symbols must come before global. + for (index, symbol) in self.symbols.iter().enumerate() { + if symbol.is_local() { + symbol_offsets[index].index = symtab_count; + symtab_count += 1; + } + } + let symtab_count_local = symtab_count; + for (index, symbol) in self.symbols.iter().enumerate() { + if !symbol.is_local() { + symbol_offsets[index].index = symtab_count; + symtab_count += 1; + } + } + for (index, symbol) in self.symbols.iter().enumerate() { + if symbol.kind != SymbolKind::Section { + symbol_offsets[index].str_id = Some(strtab.add(&symbol.name)); + } + } + + // Calculate size of symtab. + let symtab_str_id = shstrtab.add(&b".symtab"[..]); + offset = align(offset, pointer_align); + let symtab_offset = offset; + let symtab_len = symtab_count * elf::Sym::size_with(&ctx); + offset += symtab_len; + let symtab_index = e_shnum; + e_shnum += 1; + + // Calculate size of symtab_shndx. + let mut need_symtab_shndx = false; + for symbol in &self.symbols { + let index = symbol + .section + .map(|s| section_offsets[s.0].index) + .unwrap_or(0); + if index >= elf::SHN_LORESERVE as usize { + need_symtab_shndx = true; + break; + } + } + let symtab_shndx_offset = offset; + let mut symtab_shndx_str_id = None; + let mut symtab_shndx_len = 0; + if need_symtab_shndx { + symtab_shndx_str_id = Some(shstrtab.add(&b".symtab_shndx"[..])); + symtab_shndx_len = symtab_count * 4; + offset += symtab_shndx_len; + e_shnum += 1; + } + + // Calculate size of strtab. + let strtab_str_id = shstrtab.add(&b".strtab"[..]); + let strtab_offset = offset; + let mut strtab_data = Vec::new(); + // Null name. + strtab_data.push(0); + strtab.write(1, &mut strtab_data); + offset += strtab_data.len(); + let strtab_index = e_shnum; + e_shnum += 1; + + // Calculate size of relocations. + for (index, section) in self.sections.iter().enumerate() { + let count = section.relocations.len(); + if count != 0 { + offset = align(offset, pointer_align); + section_offsets[index].reloc_offset = offset; + let len = count * elf::Reloc::size_with(&reloc_ctx); + section_offsets[index].reloc_len = len; + offset += len; + } + } + + // Calculate size of shstrtab. + let shstrtab_str_id = shstrtab.add(&b".shstrtab"[..]); + let shstrtab_offset = offset; + let mut shstrtab_data = Vec::new(); + // Null section name. + shstrtab_data.push(0); + shstrtab.write(1, &mut shstrtab_data); + offset += shstrtab_data.len(); + let shstrtab_index = e_shnum; + e_shnum += 1; + + // Calculate size of section headers. + offset = align(offset, pointer_align); + let e_shoff = offset; + let e_shentsize = elf::SectionHeader::size_with(&ctx); + offset += e_shnum * e_shentsize; + + // Start writing. + let mut buffer = Vec::with_capacity(offset); + + // Write file header. + let e_machine = match self.architecture { + Architecture::Arm(_) => elf::EM_ARM, + Architecture::Aarch64(_) => elf::EM_AARCH64, + Architecture::I386 => elf::EM_386, + Architecture::X86_64 => elf::EM_X86_64, + _ => { + return Err(format!( + "unimplemented architecture {:?}", + self.architecture + )) + } + }; + let mut header = elf::Header { + e_ident: [0; 16], + e_type: elf::ET_REL, + e_machine, + e_version: elf::EV_CURRENT.into(), + e_entry: 0, + e_phoff: 0, + e_shoff: e_shoff as u64, + e_flags: 0, + e_ehsize: e_ehsize as u16, + e_phentsize: 0, + e_phnum: 0, + e_shentsize: e_shentsize as u16, + e_shnum: if e_shnum >= elf::SHN_LORESERVE as usize { + 0 + } else { + e_shnum as u16 + }, + e_shstrndx: if shstrtab_index >= elf::SHN_LORESERVE as usize { + elf::SHN_XINDEX as u16 + } else { + shstrtab_index as u16 + }, + }; + header.e_ident[0..4].copy_from_slice(elf::ELFMAG); + header.e_ident[elf::EI_CLASS] = if container.is_big() { + elf::ELFCLASS64 + } else { + elf::ELFCLASS32 + }; + header.e_ident[elf::EI_DATA] = if endian.is_little() { + elf::ELFDATA2LSB + } else { + elf::ELFDATA2MSB + }; + header.e_ident[elf::EI_VERSION] = elf::EV_CURRENT; + header.e_ident[elf::EI_OSABI] = elf::ELFOSABI_NONE; + header.e_ident[elf::EI_ABIVERSION] = 0; + buffer.iowrite_with(header, ctx).unwrap(); + + // Write section data. + for (index, section) in self.sections.iter().enumerate() { + let len = section.data.len(); + if len != 0 { + write_align(&mut buffer, section.align as usize); + debug_assert_eq!(section_offsets[index].offset, buffer.len()); + buffer.extend(§ion.data); + } + } + + // Write symbols. + write_align(&mut buffer, pointer_align); + debug_assert_eq!(symtab_offset, buffer.len()); + buffer + .iowrite_with( + elf::Sym { + st_name: 0, + st_info: 0, + st_other: 0, + st_shndx: 0, + st_value: 0, + st_size: 0, + }, + ctx, + ) + .unwrap(); + let mut symtab_shndx = Vec::new(); + if need_symtab_shndx { + symtab_shndx.iowrite_with(0, ctx.le).unwrap(); + } + let mut write_symbol = |index: usize, symbol: &Symbol| { + let st_type = match symbol.kind { + SymbolKind::Unknown | 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 { + elf::STT_OBJECT + } + } + SymbolKind::Section => elf::STT_SECTION, + SymbolKind::File => elf::STT_FILE, + SymbolKind::Common => elf::STT_COMMON, + SymbolKind::Tls => elf::STT_TLS, + SymbolKind::Label => elf::STT_NOTYPE, + }; + let st_bind = if symbol.is_undefined() { + elf::STB_GLOBAL + } else if symbol.is_local() { + elf::STB_LOCAL + } else if symbol.weak { + elf::STB_WEAK + } else { + elf::STB_GLOBAL + }; + let st_other = if symbol.scope == SymbolScope::Linkage { + elf::STV_HIDDEN + } else { + elf::STV_DEFAULT + }; + let st_shndx = match symbol.kind { + SymbolKind::File => { + if need_symtab_shndx { + symtab_shndx.iowrite_with(0, ctx.le).unwrap(); + } + elf::SHN_ABS as usize + } + _ => { + let index = symbol + .section + .map(|s| section_offsets[s.0].index) + .unwrap_or(elf::SHN_UNDEF as usize); + if need_symtab_shndx { + symtab_shndx.iowrite_with(index as u32, ctx.le).unwrap(); + } + if index >= elf::SHN_LORESERVE as usize { + elf::SHN_XINDEX as usize + } else { + index + } + } + }; + let st_name = symbol_offsets[index] + .str_id + .map(|id| strtab.get_offset(id)) + .unwrap_or(0); + buffer + .iowrite_with( + elf::Sym { + st_name, + st_info: (st_bind << 4) + st_type, + st_other, + st_shndx, + st_value: symbol.value, + st_size: symbol.size, + }, + ctx, + ) + .unwrap(); + }; + 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); + } + } + if need_symtab_shndx { + debug_assert_eq!(symtab_shndx_offset, buffer.len()); + debug_assert_eq!(symtab_shndx_len, symtab_shndx.len()); + buffer.extend(&symtab_shndx); + } + + // Write strtab section. + debug_assert_eq!(strtab_offset, buffer.len()); + buffer.extend(&strtab_data); + + // Write relocations. + for (index, section) in self.sections.iter().enumerate() { + if !section.relocations.is_empty() { + write_align(&mut buffer, pointer_align); + debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len()); + for reloc in §ion.relocations { + let r_type = match self.architecture { + 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(format!("unimplemented relocation {:?}", reloc)), + }, + Architecture::X86_64 => 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(format!("unimplemented relocation {:?}", reloc)), + }, + _ => { + return Err(format!( + "unimplemented architecture {:?}", + self.architecture + )) + } + }; + let r_sym = symbol_offsets[reloc.symbol.0].index; + buffer + .iowrite_with( + elf::Reloc { + r_offset: reloc.offset, + r_addend: Some(reloc.addend), + r_sym, + r_type, + }, + reloc_ctx, + ) + .unwrap(); + } + } + } + + // Write shstrtab section. + debug_assert_eq!(shstrtab_offset, buffer.len()); + buffer.extend(&shstrtab_data); + + // Write section headers. + write_align(&mut buffer, pointer_align); + debug_assert_eq!(e_shoff, buffer.len()); + buffer + .iowrite_with( + elf::SectionHeader { + sh_name: 0, + sh_type: 0, + sh_flags: 0, + sh_addr: 0, + sh_offset: 0, + sh_size: if e_shnum >= elf::SHN_LORESERVE as usize { + e_shnum as u64 + } else { + 0 + }, + sh_link: if shstrtab_index >= elf::SHN_LORESERVE as usize { + shstrtab_index as u32 + } else { + 0 + }, + // TODO: e_phnum overflow + sh_info: 0, + sh_addralign: 0, + sh_entsize: 0, + }, + ctx, + ) + .unwrap(); + for (index, section) in self.sections.iter().enumerate() { + let sh_type = match section.kind { + SectionKind::UninitializedData | SectionKind::UninitializedTls => elf::SHT_NOBITS, + _ => elf::SHT_PROGBITS, + }; + let sh_flags = match section.kind { + SectionKind::Text => elf::SHF_ALLOC | elf::SHF_EXECINSTR, + SectionKind::Data => 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::Unknown + | SectionKind::Metadata + | SectionKind::Linker => 0, + SectionKind::TlsVariables => { + return Err(format!("unimplemented section {:?}", section.kind)) + } + }; + // TODO: not sure if this is correct, maybe user should determine this + let sh_entsize = match section.kind { + SectionKind::ReadOnlyString | SectionKind::OtherString => 1, + _ => 0, + }; + let sh_name = section_offsets[index] + .str_id + .map(|id| shstrtab.get_offset(id)) + .unwrap_or(0); + buffer + .iowrite_with( + elf::SectionHeader { + sh_name, + sh_type, + sh_flags: sh_flags.into(), + 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, + }, + ctx, + ) + .unwrap(); + + if !section.relocations.is_empty() { + let sh_name = section_offsets[index] + .reloc_str_id + .map(|id| shstrtab.get_offset(id)) + .unwrap_or(0); + buffer + .iowrite_with( + elf::SectionHeader { + sh_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: section_offsets[index].reloc_offset as u64, + sh_size: section_offsets[index].reloc_len as u64, + sh_link: symtab_index as u32, + sh_info: section_offsets[index].index as u32, + sh_addralign: pointer_align as u64, + sh_entsize: elf::Reloc::size_with(&reloc_ctx) as u64, + }, + ctx, + ) + .unwrap(); + } + } + + // Write symtab section header. + buffer + .iowrite_with( + elf::SectionHeader { + sh_name: shstrtab.get_offset(symtab_str_id), + sh_type: elf::SHT_SYMTAB, + sh_flags: 0, + sh_addr: 0, + sh_offset: symtab_offset as u64, + sh_size: symtab_len as u64, + sh_link: strtab_index as u32, + sh_info: symtab_count_local as u32, + sh_addralign: pointer_align as u64, + sh_entsize: elf::Sym::size_with(&ctx) as u64, + }, + ctx, + ) + .unwrap(); + + // Write symtab_shndx section header. + if need_symtab_shndx { + buffer + .iowrite_with( + elf::SectionHeader { + sh_name: shstrtab.get_offset(symtab_shndx_str_id.unwrap()), + sh_type: elf::SHT_SYMTAB_SHNDX, + sh_flags: 0, + sh_addr: 0, + sh_offset: symtab_shndx_offset as u64, + sh_size: symtab_shndx_len as u64, + sh_link: strtab_index as u32, + sh_info: symtab_count_local as u32, + sh_addralign: pointer_align as u64, + sh_entsize: elf::Sym::size_with(&ctx) as u64, + }, + ctx, + ) + .unwrap(); + } + + // Write strtab section header. + buffer + .iowrite_with( + elf::SectionHeader { + sh_name: shstrtab.get_offset(strtab_str_id), + sh_type: elf::SHT_STRTAB, + sh_flags: 0, + sh_addr: 0, + sh_offset: strtab_offset as u64, + sh_size: strtab_data.len() as u64, + sh_link: 0, + sh_info: 0, + sh_addralign: 1, + sh_entsize: 0, + }, + ctx, + ) + .unwrap(); + + // Write shstrtab section header. + buffer + .iowrite_with( + elf::SectionHeader { + sh_name: shstrtab.get_offset(shstrtab_str_id), + sh_type: elf::SHT_STRTAB, + sh_flags: 0, + sh_addr: 0, + sh_offset: shstrtab_offset as u64, + sh_size: shstrtab_data.len() as u64, + sh_link: 0, + sh_info: 0, + sh_addralign: 1, + sh_entsize: 0, + }, + ctx, + ) + .unwrap(); + + Ok(buffer) + } +} 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..356a44b34d --- /dev/null +++ b/third_party/rust/object/src/write/macho.rs @@ -0,0 +1,558 @@ +use scroll::ctx::SizeWith; +use scroll::{IOwrite, Pwrite}; +use std::string::String; + +use crate::alloc::vec::Vec; +use crate::write::string::*; +use crate::write::util::*; +use crate::write::*; + +mod mach { + pub use goblin::mach::constants::cputype::*; + pub use goblin::mach::constants::*; + pub use goblin::mach::header::*; + pub use goblin::mach::load_command::*; + pub use goblin::mach::relocation::*; + pub use goblin::mach::segment::*; + pub use goblin::mach::symbols::*; +} + +#[derive(Default, Clone, Copy)] +struct SectionOffsets { + index: usize, + offset: usize, + address: u64, + reloc_offset: usize, +} + +#[derive(Default, Clone, Copy)] +struct SymbolOffsets { + index: usize, + str_id: Option<StringId>, +} + +impl Object { + 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) { + match section { + StandardSection::Text => (&b"__TEXT"[..], &b"__text"[..], SectionKind::Text), + StandardSection::Data => (&b"__DATA"[..], &b"__data"[..], SectionKind::Data), + StandardSection::ReadOnlyData => { + (&b"__TEXT"[..], &b"__const"[..], SectionKind::ReadOnlyData) + } + StandardSection::ReadOnlyDataWithRel => { + (&b"__DATA"[..], &b"__const"[..], SectionKind::ReadOnlyData) + } + StandardSection::ReadOnlyString => ( + &b"__TEXT"[..], + &b"__cstring"[..], + SectionKind::ReadOnlyString, + ), + StandardSection::UninitializedData => ( + &b"__DATA"[..], + &b"__bss"[..], + SectionKind::UninitializedData, + ), + StandardSection::Tls => (&b"__DATA"[..], &b"__thread_data"[..], SectionKind::Tls), + StandardSection::UninitializedTls => ( + &b"__DATA"[..], + &b"__thread_bss"[..], + SectionKind::UninitializedTls, + ), + StandardSection::TlsVariables => ( + &b"__DATA"[..], + &b"__thread_vars"[..], + SectionKind::TlsVariables, + ), + } + } + + 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: 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(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: 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 pointer_width = self.architecture.pointer_width().unwrap().bytes(); + let size = u64::from(pointer_width) * 3; + let data = vec![0; size as usize]; + let offset = self.append_section_data(section, &data, u64::from(pointer_width)); + + let tlv_bootstrap = self.macho_tlv_bootstrap(); + self.add_relocation( + section, + Relocation { + offset: offset, + size: pointer_width * 8, + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + symbol: tlv_bootstrap, + addend: 0, + }, + ) + .unwrap(); + self.add_relocation( + section, + Relocation { + offset: offset + u64::from(pointer_width) * 2, + size: pointer_width * 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 = Some(section); + + init_symbol_id + } + + pub(crate) fn macho_fixup_relocation(&mut self, mut relocation: &mut Relocation) -> i64 { + let constant = match relocation.kind { + RelocationKind::Relative + | RelocationKind::GotRelative + | RelocationKind::PltRelative => relocation.addend + 4, + _ => relocation.addend, + }; + relocation.addend -= constant; + constant + } + + pub(crate) fn macho_write(&self) -> Result<Vec<u8>, String> { + let endian = match self.architecture.endianness().unwrap() { + Endianness::Little => goblin::container::Endian::Little, + Endianness::Big => goblin::container::Endian::Big, + }; + let (container, pointer_align) = match self.architecture.pointer_width().unwrap() { + PointerWidth::U16 | PointerWidth::U32 => (goblin::container::Container::Little, 4), + PointerWidth::U64 => (goblin::container::Container::Big, 8), + }; + let ctx = goblin::container::Ctx::new(container, endian); + + // Calculate offsets of everything, and build strtab. + let mut offset = 0; + + // Calculate size of Mach-O header. + offset += mach::Header::size_with(&ctx); + + // Calculate size of commands. + let mut ncmds = 0; + let command_offset = offset; + + // Calculate size of segment command and section headers. + let segment_command_offset = offset; + let segment_command_len = + mach::Segment::size_with(&ctx) + self.sections.len() * mach::Section::size_with(&ctx); + offset += segment_command_len; + ncmds += 1; + + // Calculate size of symtab command. + let symtab_command_offset = offset; + let symtab_command_len = mach::SymtabCommand::size_with(&ctx.le); + offset += symtab_command_len; + ncmds += 1; + + let sizeofcmds = offset - command_offset; + + // Calculate size of section data. + let segment_data_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; + let len = section.data.len(); + if len != 0 { + offset = align(offset, section.align as usize); + section_offsets[index].offset = offset; + offset += len; + } else { + section_offsets[index].offset = offset; + } + address = align_u64(address, section.align); + section_offsets[index].address = address; + address += section.size; + } + let segment_data_size = offset - segment_data_offset; + + // 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() { + if !symbol.is_undefined() { + match symbol.kind { + SymbolKind::Text | SymbolKind::Data | SymbolKind::Tls => {} + SymbolKind::File | SymbolKind::Section => continue, + _ => return Err(format!("unimplemented symbol {:?}", symbol)), + } + } + 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 * mach::Nlist::size_with(&ctx); + offset += symtab_len; + + // Calculate size of strtab. + let strtab_offset = offset; + let mut strtab_data = Vec::new(); + // Null name. + strtab_data.push(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 * mach::RelocationInfo::size_with(&ctx.le); + offset += len; + } + } + + // Start writing. + let mut buffer = Vec::with_capacity(offset); + + // Write file header. + let (cputype, cpusubtype) = match self.architecture { + Architecture::Arm(_) => (mach::CPU_TYPE_ARM, mach::CPU_SUBTYPE_ARM_ALL), + Architecture::Aarch64(_) => (mach::CPU_TYPE_ARM64, mach::CPU_SUBTYPE_ARM64_ALL), + Architecture::I386 => (mach::CPU_TYPE_I386, mach::CPU_SUBTYPE_I386_ALL), + Architecture::X86_64 => (mach::CPU_TYPE_X86_64, mach::CPU_SUBTYPE_X86_64_ALL), + _ => { + return Err(format!( + "unimplemented architecture {:?}", + self.architecture + )) + } + }; + + let header = mach::Header { + magic: if ctx.is_big() { + mach::MH_MAGIC_64 + } else { + mach::MH_MAGIC + }, + cputype, + cpusubtype, + filetype: mach::MH_OBJECT, + ncmds, + sizeofcmds: sizeofcmds as u32, + flags: if self.subsection_via_symbols { + mach::MH_SUBSECTIONS_VIA_SYMBOLS + } else { + 0 + }, + reserved: 0, + }; + buffer.iowrite_with(header, ctx).unwrap(); + + // Write segment command. + debug_assert_eq!(segment_command_offset, buffer.len()); + let mut segment_command = mach::Segment::new(ctx, &[]); + segment_command.cmd = if ctx.is_big() { + mach::LC_SEGMENT_64 + } else { + mach::LC_SEGMENT + }; + segment_command.cmdsize = segment_command_len as u32; + segment_command.segname = [0; 16]; + segment_command.vmaddr = 0; + segment_command.vmsize = address; + segment_command.fileoff = segment_data_offset as u64; + segment_command.filesize = segment_data_size as u64; + segment_command.maxprot = mach::VM_PROT_READ | mach::VM_PROT_WRITE | mach::VM_PROT_EXECUTE; + segment_command.initprot = mach::VM_PROT_READ | mach::VM_PROT_WRITE | mach::VM_PROT_EXECUTE; + segment_command.nsects = self.sections.len() as u32; + segment_command.flags = 0; + buffer.iowrite_with(segment_command, ctx).unwrap(); + + // Write section headers. + for (index, section) in self.sections.iter().enumerate() { + let mut sectname = [0; 16]; + sectname.pwrite(&*section.name, 0).unwrap(); + let mut segname = [0; 16]; + segname.pwrite(&*section.segment, 0).unwrap(); + let flags = match section.kind { + SectionKind::Text => { + mach::S_ATTR_PURE_INSTRUCTIONS | mach::S_ATTR_SOME_INSTRUCTIONS + } + SectionKind::Data => 0, + SectionKind::ReadOnlyData => 0, + SectionKind::ReadOnlyString => mach::S_CSTRING_LITERALS, + SectionKind::UninitializedData => mach::S_ZEROFILL, + SectionKind::Tls => mach::S_THREAD_LOCAL_REGULAR, + SectionKind::UninitializedTls => mach::S_THREAD_LOCAL_ZEROFILL, + SectionKind::TlsVariables => mach::S_THREAD_LOCAL_VARIABLES, + SectionKind::Debug => mach::S_ATTR_DEBUG, + SectionKind::OtherString => mach::S_CSTRING_LITERALS, + SectionKind::Other + | SectionKind::Unknown + | SectionKind::Linker + | SectionKind::Metadata => 0, + }; + buffer + .iowrite_with( + mach::Section { + 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, + }, + ctx, + ) + .unwrap(); + } + + // Write symtab command. + debug_assert_eq!(symtab_command_offset, buffer.len()); + buffer + .iowrite_with( + mach::SymtabCommand { + cmd: mach::LC_SYMTAB, + cmdsize: symtab_command_len as u32, + symoff: symtab_offset as u32, + nsyms: nsyms as u32, + stroff: strtab_offset as u32, + strsize: strtab_data.len() as u32, + }, + ctx.le, + ) + .unwrap(); + + // Write section data. + debug_assert_eq!(segment_data_offset, buffer.len()); + for (index, section) in self.sections.iter().enumerate() { + let len = section.data.len(); + if len != 0 { + write_align(&mut buffer, section.align as usize); + debug_assert_eq!(section_offsets[index].offset, buffer.len()); + buffer.extend(§ion.data); + } + } + + // Write symtab. + write_align(&mut buffer, pointer_align); + debug_assert_eq!(symtab_offset, buffer.len()); + for (index, symbol) in self.symbols.iter().enumerate() { + if !symbol.is_undefined() { + match symbol.kind { + SymbolKind::Text | SymbolKind::Data | SymbolKind::Tls => {} + SymbolKind::File | SymbolKind::Section => continue, + _ => return Err(format!("unimplemented symbol {:?}", symbol)), + } + } + // TODO: N_STAB + // TODO: N_ABS + let mut n_type = if symbol.is_undefined() { + mach::N_UNDF | mach::N_EXT + } else { + mach::N_SECT + }; + match symbol.scope { + SymbolScope::Unknown | SymbolScope::Compilation => {} + SymbolScope::Linkage => { + n_type |= mach::N_EXT | mach::N_PEXT; + } + SymbolScope::Dynamic => { + n_type |= mach::N_EXT; + } + } + + let mut n_desc = 0; + if symbol.weak { + if symbol.is_undefined() { + n_desc |= mach::N_WEAK_REF; + } else { + n_desc |= mach::N_WEAK_DEF; + } + } + + let n_value = match symbol.section { + 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); + + buffer + .iowrite_with( + mach::Nlist { + n_strx, + n_type, + n_sect: symbol.section.map(|x| x.0 + 1).unwrap_or(0), + n_desc, + n_value, + }, + ctx, + ) + .unwrap(); + } + + // Write strtab. + debug_assert_eq!(strtab_offset, buffer.len()); + buffer.extend(&strtab_data); + + // Write relocations. + for (index, section) in self.sections.iter().enumerate() { + if !section.relocations.is_empty() { + write_align(&mut buffer, 4); + debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len()); + for reloc in §ion.relocations { + let r_extern; + let r_symbolnum; + let symbol = &self.symbols[reloc.symbol.0]; + if symbol.kind == SymbolKind::Section { + r_symbolnum = section_offsets[symbol.section.unwrap().0].index as u32; + r_extern = 0; + } else { + r_symbolnum = symbol_offsets[reloc.symbol.0].index as u32; + r_extern = 1; + } + let r_length = match reloc.size { + 8 => 0, + 16 => 1, + 32 => 2, + 64 => 3, + _ => return Err(format!("unimplemented reloc size {:?}", reloc)), + }; + let (r_pcrel, r_type) = match self.architecture { + Architecture::I386 => match reloc.kind { + RelocationKind::Absolute => (0, mach::GENERIC_RELOC_VANILLA), + _ => return Err(format!("unimplemented relocation {:?}", reloc)), + }, + Architecture::X86_64 => match (reloc.kind, reloc.encoding, reloc.addend) { + (RelocationKind::Absolute, RelocationEncoding::Generic, 0) => { + (0, mach::X86_64_RELOC_UNSIGNED) + } + (RelocationKind::Relative, RelocationEncoding::Generic, -4) => { + (1, mach::X86_64_RELOC_SIGNED) + } + (RelocationKind::Relative, RelocationEncoding::X86RipRelative, -4) => { + (1, mach::X86_64_RELOC_SIGNED) + } + (RelocationKind::Relative, RelocationEncoding::X86Branch, -4) => { + (1, mach::X86_64_RELOC_BRANCH) + } + (RelocationKind::PltRelative, RelocationEncoding::X86Branch, -4) => { + (1, mach::X86_64_RELOC_BRANCH) + } + (RelocationKind::GotRelative, RelocationEncoding::Generic, -4) => { + (1, mach::X86_64_RELOC_GOT) + } + ( + RelocationKind::GotRelative, + RelocationEncoding::X86RipRelativeMovq, + -4, + ) => (1, mach::X86_64_RELOC_GOT_LOAD), + (RelocationKind::MachO { value, relative }, _, _) => { + (u32::from(relative), value) + } + _ => return Err(format!("unimplemented relocation {:?}", reloc)), + }, + _ => { + return Err(format!( + "unimplemented architecture {:?}", + self.architecture + )) + } + }; + let r_info = r_symbolnum + | r_pcrel << 24 + | r_length << 25 + | r_extern << 27 + | u32::from(r_type) << 28; + buffer + .iowrite_with( + mach::RelocationInfo { + r_address: reloc.offset as i32, + r_info, + }, + ctx.le, + ) + .unwrap(); + } + } + } + + Ok(buffer) + } +} 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..79ffc16599 --- /dev/null +++ b/third_party/rust/object/src/write/mod.rs @@ -0,0 +1,674 @@ +//! Interface for writing object files. + +#![allow(clippy::collapsible_if)] +#![allow(clippy::cognitive_complexity)] +#![allow(clippy::module_inception)] + +use scroll::Pwrite; +use std::collections::HashMap; +use std::string::String; + +use crate::alloc::vec::Vec; +use crate::target_lexicon::{Architecture, BinaryFormat, Endianness, PointerWidth}; +use crate::{RelocationEncoding, RelocationKind, SectionKind, SymbolKind, SymbolScope}; + +mod coff; +mod elf; +mod macho; +mod string; +mod util; + +/// A writable object file. +#[derive(Debug)] +pub struct Object { + format: BinaryFormat, + architecture: Architecture, + sections: Vec<Section>, + standard_sections: HashMap<StandardSection, SectionId>, + symbols: Vec<Symbol>, + symbol_map: HashMap<Vec<u8>, SymbolId>, + stub_symbols: HashMap<SymbolId, SymbolId>, + subsection_via_symbols: bool, + /// The symbol name mangling scheme. + pub mangling: Mangling, + /// Mach-O "_tlv_bootstrap" symbol. + tlv_bootstrap: Option<SymbolId>, +} + +impl Object { + /// Create an empty object file. + pub fn new(format: BinaryFormat, architecture: Architecture) -> Object { + Object { + format, + architecture, + sections: Vec::new(), + standard_sections: HashMap::new(), + symbols: Vec::new(), + symbol_map: HashMap::new(), + stub_symbols: HashMap::new(), + subsection_via_symbols: false, + mangling: Mangling::default(format, architecture), + tlv_bootstrap: 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 + } + + /// Return the current 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. + pub fn segment_name(&self, segment: StandardSegment) -> &'static [u8] { + match self.format { + BinaryFormat::Elf | BinaryFormat::Coff => &[], + BinaryFormat::Macho => self.macho_segment_name(segment), + _ => unimplemented!(), + } + } + + /// Get the section with the given `SectionId`. + #[inline] + pub fn section(&self, section: SectionId) -> &Section { + &self.sections[section.0] + } + + /// Mutably get the section with the given `SectionId`. + #[inline] + pub fn section_mut(&mut self, section: SectionId) -> &mut Section { + &mut self.sections[section.0] + } + + /// 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) = self.section_info(section); + self.add_section(segment.to_vec(), name.to_vec(), kind) + }) + } + + /// 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: Vec::new(), + relocations: Vec::new(), + symbol: 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) = 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) { + match self.format { + BinaryFormat::Elf => self.elf_section_info(section), + BinaryFormat::Coff => self.coff_section_info(section), + BinaryFormat::Macho => self.macho_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_subsection_via_symbols() { + self.subsection_via_symbols = true; + self.section_id(section) + } else { + let (segment, name, kind) = self.subsection_info(section, name); + self.add_section(segment.to_vec(), name, kind) + }; + let offset = self.append_section_data(section_id, data, align); + (section_id, offset) + } + + fn has_subsection_via_symbols(&self) -> bool { + match self.format { + BinaryFormat::Elf | BinaryFormat::Coff => false, + BinaryFormat::Macho => true, + _ => unimplemented!(), + } + } + + fn subsection_info( + &self, + section: StandardSection, + value: &[u8], + ) -> (&'static [u8], Vec<u8>, SectionKind) { + let (segment, section, kind) = self.section_info(section); + let name = self.subsection_name(section, value); + (segment, name, kind) + } + + fn subsection_name(&self, section: &[u8], value: &[u8]) -> Vec<u8> { + debug_assert!(!self.has_subsection_via_symbols()); + match self.format { + BinaryFormat::Elf => self.elf_subsection_name(section, value), + BinaryFormat::Coff => self.coff_subsection_name(section, value), + _ => unimplemented!(), + } + } + + /// 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 { + return self.section_symbol(symbol.section.unwrap()); + } + 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`. + pub fn has_uninitialized_tls(&self) -> bool { + self.format != BinaryFormat::Coff + } + + /// 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: 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: Some(section_id), + }); + 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. + 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); + if self.format == 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 = Some(section); + } + + /// Convert a symbol to a section symbol and offset. + /// + /// Returns an error if the symbol is not defined. + pub fn symbol_section_and_offset( + &mut self, + symbol_id: SymbolId, + ) -> Result<(SymbolId, u64), ()> { + let symbol = self.symbol(symbol_id); + if symbol.kind == SymbolKind::Section { + return Ok((symbol_id, 0)); + } + let symbol_offset = symbol.value; + let section = symbol.section.ok_or(())?; + let section_symbol = self.section_symbol(section); + Ok((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<(), String> { + let addend = match self.format { + BinaryFormat::Elf => self.elf_fixup_relocation(&mut relocation)?, + BinaryFormat::Coff => self.coff_fixup_relocation(&mut relocation), + BinaryFormat::Macho => self.macho_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<(), String> { + let endian = match self.architecture.endianness().unwrap() { + Endianness::Little => scroll::LE, + Endianness::Big => scroll::BE, + }; + + let data = &mut self.sections[section.0].data; + if relocation.offset + (u64::from(relocation.size) + 7) / 8 > data.len() as u64 { + return Err(format!( + "invalid relocation offset {}+{} (max {})", + relocation.offset, + relocation.size, + data.len() + )); + } + match relocation.size { + 32 => { + data.pwrite_with(addend as i32, relocation.offset as usize, endian) + .unwrap(); + } + 64 => { + data.pwrite_with(addend, relocation.offset as usize, endian) + .unwrap(); + } + _ => return Err(format!("unimplemented relocation addend {:?}", relocation)), + } + Ok(()) + } + + /// Write the object to a `Vec`. + pub fn write(&self) -> Result<Vec<u8>, String> { + match self.format { + BinaryFormat::Elf => self.elf_write(), + BinaryFormat::Coff => self.coff_write(), + BinaryFormat::Macho => self.macho_write(), + _ => unimplemented!(), + } + } +} + +/// A standard segment kind. +#[allow(missing_docs)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum StandardSegment { + Text, + Data, + Debug, +} + +/// A standard section kind. +#[allow(missing_docs)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +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, +} + +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 | StandardSection::ReadOnlyDataWithRel => { + SectionKind::ReadOnlyData + } + StandardSection::ReadOnlyString => SectionKind::ReadOnlyString, + StandardSection::UninitializedData => SectionKind::UninitializedData, + StandardSection::Tls => SectionKind::Tls, + StandardSection::UninitializedTls => SectionKind::UninitializedTls, + StandardSection::TlsVariables => SectionKind::TlsVariables, + } + } + + fn all() -> &'static [StandardSection] { + &[ + StandardSection::Text, + StandardSection::Data, + StandardSection::ReadOnlyData, + StandardSection::ReadOnlyDataWithRel, + StandardSection::ReadOnlyString, + StandardSection::UninitializedData, + StandardSection::Tls, + StandardSection::UninitializedTls, + StandardSection::TlsVariables, + ] + } +} + +/// 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 { + segment: Vec<u8>, + name: Vec<u8>, + kind: SectionKind, + size: u64, + align: u64, + data: Vec<u8>, + relocations: Vec<Relocation>, + symbol: Option<SymbolId>, +} + +impl Section { + /// Return true if this section contains uninitialized data. + #[inline] + pub fn is_bss(&self) -> bool { + self.kind == SectionKind::UninitializedData || self.kind == SectionKind::UninitializedTls + } + + /// 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(&mut self, data: Vec<u8>, align: u64) { + debug_assert!(!self.is_bss()); + debug_assert_eq!(align & (align - 1), 0); + debug_assert!(self.data.is_empty()); + self.size = data.len() as u64; + self.data = data; + self.align = align; + } + + /// Append data to a section. + /// + /// Must not be called for sections that contain uninitialized data. + pub fn append_data(&mut self, 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 mut offset = self.data.len(); + if offset & (align - 1) != 0 { + offset += align - (offset & (align - 1)); + self.data.resize(offset, 0); + } + self.data.extend(data); + self.size = self.data.len() as u64; + offset as u64 + } + + /// Append unitialized 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 as u64 + } +} + +/// 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. + /// + /// Set to `None` for undefined symbols. + pub section: Option<SectionId>, +} + +impl Symbol { + /// Return true if the symbol is undefined. + #[inline] + pub fn is_undefined(&self) -> bool { + self.section.is_none() + } + + /// 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, +} + +/// The symbol name mangling scheme. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +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, +} + +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, + _ => 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 => None, + Mangling::CoffI386 | Mangling::Macho => Some(b'_'), + } + } +} 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..6a7ac31fe6 --- /dev/null +++ b/third_party/rust/object/src/write/string.rs @@ -0,0 +1,140 @@ +use indexmap::IndexSet; + +use crate::alloc::vec::Vec; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) 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 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..0ac65424af --- /dev/null +++ b/third_party/rust/object/src/write/util.rs @@ -0,0 +1,14 @@ +use crate::alloc::vec::Vec; + +pub(crate) fn align(offset: usize, size: usize) -> usize { + (offset + (size - 1)) & !(size - 1) +} + +pub(crate) fn align_u64(offset: u64, size: u64) -> u64 { + (offset + (size - 1)) & !(size - 1) +} + +pub(crate) fn write_align(buffer: &mut Vec<u8>, size: usize) { + let offset = align(buffer.len(), size); + buffer.resize(offset, 0); +} diff --git a/third_party/rust/object/tests/round_trip.rs b/third_party/rust/object/tests/round_trip.rs new file mode 100644 index 0000000000..7e63c4d85a --- /dev/null +++ b/third_party/rust/object/tests/round_trip.rs @@ -0,0 +1,277 @@ +#![cfg(all(feature = "read", feature = "write"))] + +use object::read::{Object, ObjectSection}; +use object::{read, write}; +use object::{RelocationEncoding, RelocationKind, SectionKind, SymbolKind, SymbolScope}; +use target_lexicon::{Architecture, BinaryFormat}; + +#[test] +fn coff_x86_64() { + let mut object = write::Object::new(BinaryFormat::Coff, Architecture::X86_64); + + 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: Some(text), + }); + 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); + + let mut sections = object.sections(); + + let text = sections.next().unwrap(); + println!("{:?}", text); + let text_index = text.index(); + assert_eq!(text.name(), Some(".text")); + assert_eq!(text.kind(), SectionKind::Text); + assert_eq!(text.address(), 0); + assert_eq!(text.size(), 62); + assert_eq!(&text.data()[..30], &[1; 30]); + assert_eq!(&text.data()[32..62], &[1; 30]); + + let mut symbols = object.symbols(); + + let (func1_symbol, symbol) = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Some("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); +} + +#[test] +fn elf_x86_64() { + let mut object = write::Object::new(BinaryFormat::Elf, Architecture::X86_64); + + 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: Some(text), + }); + 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); + + let mut sections = object.sections(); + + let section = sections.next().unwrap(); + println!("{:?}", text); + assert_eq!(section.name(), Some("")); + 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(), Some(".text")); + assert_eq!(text.kind(), SectionKind::Text); + assert_eq!(text.address(), 0); + assert_eq!(text.size(), 62); + assert_eq!(&text.data()[..30], &[1; 30]); + assert_eq!(&text.data()[32..62], &[1; 30]); + + let mut symbols = object.symbols(); + + let (_, symbol) = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Some("")); + 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 (func1_symbol, symbol) = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Some("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); +} + +#[test] +fn macho_x86_64() { + let mut object = write::Object::new(BinaryFormat::Macho, Architecture::X86_64); + + 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: Some(text), + }); + 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); + + let mut sections = object.sections(); + + let text = sections.next().unwrap(); + println!("{:?}", text); + let text_index = text.index(); + assert_eq!(text.name(), Some("__text")); + assert_eq!(text.segment_name(), Some("__TEXT")); + assert_eq!(text.kind(), SectionKind::Text); + assert_eq!(text.address(), 0); + assert_eq!(text.size(), 62); + assert_eq!(&text.data()[..30], &[1; 30]); + assert_eq!(&text.data()[32..62], &[1; 30]); + + let mut symbols = object.symbols(); + + let (func1_symbol, symbol) = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Some("_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); +} diff --git a/third_party/rust/object/tests/tls.rs b/third_party/rust/object/tests/tls.rs new file mode 100644 index 0000000000..653b7e714f --- /dev/null +++ b/third_party/rust/object/tests/tls.rs @@ -0,0 +1,167 @@ +#![cfg(all(feature = "read", feature = "write"))] + +use object::read::{Object, ObjectSection}; +use object::{read, write}; +use object::{RelocationEncoding, RelocationKind, SectionKind, SymbolKind, SymbolScope}; +use target_lexicon::{Architecture, BinaryFormat}; + +#[test] +fn macho_x86_64_tls() { + let mut object = write::Object::new(BinaryFormat::Macho, Architecture::X86_64); + + 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: 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: 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!("{:?}", section); + let thread_data_index = thread_data.index(); + assert_eq!(thread_data.name(), Some("__thread_data")); + assert_eq!(thread_data.segment_name(), Some("__DATA")); + assert_eq!(thread_data.kind(), SectionKind::Tls); + assert_eq!(thread_data.size(), 30); + assert_eq!(&thread_data.data()[..], &[1; 30]); + + let thread_vars = sections.next().unwrap(); + println!("{:?}", section); + let thread_vars_index = thread_vars.index(); + assert_eq!(thread_vars.name(), Some("__thread_vars")); + assert_eq!(thread_vars.segment_name(), Some("__DATA")); + assert_eq!(thread_vars.kind(), SectionKind::TlsVariables); + assert_eq!(thread_vars.size(), 2 * 3 * 8); + + let thread_bss = sections.next().unwrap(); + println!("{:?}", section); + let thread_bss_index = thread_bss.index(); + assert_eq!(thread_bss.name(), Some("__thread_bss")); + assert_eq!(thread_bss.segment_name(), Some("__DATA")); + assert_eq!(thread_bss.kind(), SectionKind::UninitializedTls); + assert_eq!(thread_bss.size(), 31); + + let mut symbols = object.symbols(); + + let (_, symbol) = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Some("_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 (tls1_init_symbol, symbol) = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Some("_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 (tlv_bootstrap_symbol, symbol) = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Some("__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(), Some("_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 (tls2_init_symbol, symbol) = symbols.next().unwrap(); + println!("{:?}", symbol); + assert_eq!(symbol.name(), Some("_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); +} |