summaryrefslogtreecommitdiffstats
path: root/third_party/rust/object
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/object')
-rw-r--r--third_party/rust/object/.cargo-checksum.json1
-rw-r--r--third_party/rust/object/Cargo.lock246
-rw-r--r--third_party/rust/object/Cargo.toml69
-rw-r--r--third_party/rust/object/LICENSE-APACHE201
-rw-r--r--third_party/rust/object/LICENSE-MIT25
-rw-r--r--third_party/rust/object/README.md3
-rw-r--r--third_party/rust/object/examples/nm.rs92
-rw-r--r--third_party/rust/object/examples/objcopy.rs117
-rw-r--r--third_party/rust/object/examples/objdump.rs77
-rw-r--r--third_party/rust/object/src/common.rs187
-rw-r--r--third_party/rust/object/src/lib.rs45
-rw-r--r--third_party/rust/object/src/read/any.rs523
-rw-r--r--third_party/rust/object/src/read/coff.rs527
-rw-r--r--third_party/rust/object/src/read/elf.rs709
-rw-r--r--third_party/rust/object/src/read/macho.rs615
-rw-r--r--third_party/rust/object/src/read/mod.rs250
-rw-r--r--third_party/rust/object/src/read/pe.rs407
-rw-r--r--third_party/rust/object/src/read/traits.rs220
-rw-r--r--third_party/rust/object/src/read/wasm.rs315
-rw-r--r--third_party/rust/object/src/write/coff.rs486
-rw-r--r--third_party/rust/object/src/write/elf.rs736
-rw-r--r--third_party/rust/object/src/write/macho.rs558
-rw-r--r--third_party/rust/object/src/write/mod.rs674
-rw-r--r--third_party/rust/object/src/write/string.rs140
-rw-r--r--third_party/rust/object/src/write/util.rs14
-rw-r--r--third_party/rust/object/tests/round_trip.rs277
-rw-r--r--third_party/rust/object/tests/tls.rs167
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, &section_kinds);
+ }
+ println!();
+
+ println!("Dynamic symbols:");
+ for (_, symbol) in file.dynamic_symbols() {
+ print_symbol(&symbol, &section_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(&section).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_{}", &section_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("__")
+ && &section_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(&section.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(&section.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(&section.data);
+ }
+
+ if !section.relocations.is_empty() {
+ debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len());
+ for reloc in &section.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(&section.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(&section.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(&section.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(&section.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 &section.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(&section.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 &section.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(&section)
+ .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);
+}