chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

View File

@@ -0,0 +1 @@
{"files":{".cargo_vcs_info.json":"8b770e3d17762370f50956d245d92378becb47a09fc95dd1c46106ab4792a00f",".github/workflows/ci.yml":"52071d42c1698c1c0b8480092f0fddb735cb56443a40816edabf0cd958826460",".github/workflows/publish.yml":"50fe68b5575b7a0ca21c329eab46d3e89c27f37401fdc8d805251c769a1db6b2","Cargo.lock":"54bdb5bfbba08558ef7e526a65ce8e19022f87524594327ec7bced60cca6af3a","Cargo.toml":"285cd0762ce6312369bf042fa1e61af8c583535780068ae71a00a51d86377e84","Cargo.toml.orig":"f9b8aa1c237d8f63f7725f26e9de7c26a051bdd876b5ff6562ca4a2a530e5d53","LICENSE.txt":"f72b120d1385408e9e380acc020756eb6ba1b461d66c328ea67327ba08d7dcfd","Readme.md":"f8dfc1f489a94ff054e6cc3467ce9388850ac20b4defbe9c92881cdffa90e1ed","reference/Alignment.h":"bc3993981c10cd5b9e5520364828bed29ad46683867b2b957db8f6a24bb35fa0","reference/Archive.h":"c1e6070e58ecadd6e78df119a6b00286ea46a632e31bc50477504df14b9f28ab","reference/ArchiveWriter.cpp":"a4ac2863d42d42602edc6ba2917a995e1d72556d2ee2ec3ef94d93f14cca0f92","reference/ArchiveWriter.h":"a7606d5f8b091fab00e181729057d83905e724e8cded892b1929186781b0710b","reference/COFF.h":"8f5b5cbf461483cf681099de73c039bf7f1d852dfa58597da583c631e56f4f20","reference/COFFImportFile.cpp":"3618e726ac71950fcc9d346b6e07256b97ba9123402feb00283a18fb06b7bc68","reference/COFFImportFile.h":"e96220f335b5dcdb1961af5d2f48a2c8eec45be58c13ad7d5d209baa0ffc6a05","reference/Mangler.cpp":"bfc075658e0ce73cba00c04fb9b5ac81b206ebca1385a06a0d14a5a1268c450e","reference/ManglerTest.cpp":"b888ed141eb6cbd8754e690ba56d61bf81be0cdb79aff318b1235d91c77057dc","reference/MathExtras.h":"47e3c67406bcbc3715e18c52c52349efeda48abe7e1b40f8fa824c478a631336","reference/Readme.md":"dd22d426f4698eaa2fbef714f3a89161c76e68af55198a9bee0ad2e11e4dbe58","rust-toolchain.toml":"d47ba4b803cd7b3e37fcb14f1120494097eef50ec33c9da094fbd556984ddd1e","src/alignment.rs":"5c0d8be73dd22cd165df734f3b17955ff2247a4a25eb3ebd41c6008bbad970cd","src/archive.rs":"077eaace42cd8b7fd2d07e0e507b5d87194c5c99d4d35e0fcade242e7eb6e490","src/archive_writer.rs":"300e067dab13914daed836603232a1c7c565073dc41a34194c4b210cac6602fc","src/coff.rs":"fda9e56cbf1cca74f3f746c72c611fe418ec2525bcdacddaec73d9b022300d75","src/coff_import_file.rs":"e6f3bee7ab877cfb21dd7eab34296860373b417f12bc96fbfae06ba97e4c7fcc","src/lib.rs":"7aa726d31dddd8b7717dd3b6225dd98ae2093f6c2d775a362af9a8f7580ca7a5","src/mangler.rs":"1ddbf1c49293cf7c1a8e896684b7a1e84cfbb52aeb6322c3730e002c6f5503f6","src/math_extras.rs":"6fbd1366d2e2d1254161c0c27f4cb8e5069ceb8183d5c8ffd5539ae5e2d747f6","src/object_reader.rs":"e04681e2495ee7421db639ad21cb06a64db6dd7f754ca8e0764a8b7819985291","tests/common.rs":"ee13b7d0cae85e33088035564ab93d375dd5bef4ba9dd9ff5c5c2ff262bd009a","tests/import_library.def":"7e77bed3980482449bd9c96a1c19dc62fd0df0ba7eecce75cfd2ff15d220101f","tests/import_library.rs":"61605349dca35594afd0ecdd9898ca1f2697d8c993fbb23c7c0249f89cd61c17","tests/multiple_objects.rs":"90adf600f73e905d39c5e72ea4d230c64665140603f7fce6efe16f560f3434c3","tests/round_trip.rs":"d8584631d6669596a51936f4fb364d7d84d1d97e14b91d6ce377d1b83e0a607f"},"package":"7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b"}

View File

@@ -0,0 +1,6 @@
{
"git": {
"sha1": "7473c3b3fe034a10d3023e363f5c38e0fabc2a02"
},
"path_in_vcs": ""
}

View File

@@ -0,0 +1,38 @@
on: [push, pull_request]
name: CI
jobs:
test:
name: test
runs-on: ubuntu-latest
timeout-minutes: 10
env:
RUSTFLAGS: -Dwarnings
RUST_BACKTRACE: full
steps:
- uses: actions/checkout@v3
- name: Clippy
run: |
cargo clippy -- -Dwarnings
- name: Test
run: |
cargo test
- name: Check (big-endian)
run: |
rustup target add powerpc64-unknown-linux-gnu
cargo check --target powerpc64-unknown-linux-gnu
fmt:
name: rustfmt
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v3
- name: Install rustfmt
run: |
rustup component add rustfmt
- name: Rustfmt
run: |
cargo fmt --check

View File

@@ -0,0 +1,21 @@
name: Publish to crates.io
on:
push:
tags: ['v*']
jobs:
publish:
runs-on: ubuntu-latest
environment: publish
permissions:
id-token: write
steps:
- uses: actions/checkout@v4
- uses: rust-lang/crates-io-auth-action@v1
id: auth
- run: cargo publish
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}

585
vendor/ar_archive_writer/Cargo.lock generated vendored Normal file
View File

@@ -0,0 +1,585 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "addr2line"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"gimli",
]
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "anyhow"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
[[package]]
name = "ar_archive_writer"
version = "0.5.1"
dependencies = [
"cargo-binutils",
"object 0.37.3",
"pretty_assertions",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "backtrace"
version = "0.3.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object 0.36.7",
"rustc-demangle",
"windows-targets",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "camino"
version = "1.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d07aa9a93b00c76f71bc35d598bed923f6d4f3a9ca5c24b7737ae1a292841c0"
dependencies = [
"serde",
]
[[package]]
name = "cargo-binutils"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec9a0e2ad2c6ec1516256bf9d0f07841a55265f2167e5d1c88ed1f99c73900da"
dependencies = [
"anyhow",
"cargo_metadata",
"clap",
"regex",
"rustc-cfg",
"rustc-demangle",
"rustc_version",
"serde",
"toml",
]
[[package]]
name = "cargo-platform"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea"
dependencies = [
"serde",
]
[[package]]
name = "cargo_metadata"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa"
dependencies = [
"camino",
"cargo-platform",
"semver",
"serde",
"serde_json",
]
[[package]]
name = "cfg-if"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
[[package]]
name = "clap"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "crc32fast"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
"cfg-if",
]
[[package]]
name = "diff"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "failure"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
dependencies = [
"backtrace",
"failure_derive",
]
[[package]]
name = "failure_derive"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"synstructure",
]
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "gimli"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "indexmap"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "libc"
version = "0.2.175"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
[[package]]
name = "memchr"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
]
[[package]]
name = "object"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"memchr",
]
[[package]]
name = "object"
version = "0.37.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
dependencies = [
"crc32fast",
"hashbrown",
"indexmap",
"memchr",
]
[[package]]
name = "pretty_assertions"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d"
dependencies = [
"diff",
"yansi",
]
[[package]]
name = "proc-macro2"
version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
[[package]]
name = "rustc-cfg"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ad221fe7cd09334f8735dcc157b1178e343f43dfaefcd1b09d7fd4fc0921b6f"
dependencies = [
"failure",
]
[[package]]
name = "rustc-demangle"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "semver"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
dependencies = [
"serde",
]
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.106",
]
[[package]]
name = "serde_json"
version = "1.0.143"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "synstructure"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"unicode-xid",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "toml"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
dependencies = [
"serde",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "unicode-width"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "yansi"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"

75
vendor/ar_archive_writer/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,75 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2024"
name = "ar_archive_writer"
version = "0.5.1"
build = false
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "A writer for object file ar archives"
readme = "Readme.md"
keywords = [
"ar",
"archive",
]
license = "Apache-2.0 WITH LLVM-exception"
repository = "https://github.com/rust-lang/ar_archive_writer"
[lib]
name = "ar_archive_writer"
path = "src/lib.rs"
[[test]]
name = "common"
path = "tests/common.rs"
[[test]]
name = "import_library"
path = "tests/import_library.rs"
[[test]]
name = "multiple_objects"
path = "tests/multiple_objects.rs"
[[test]]
name = "round_trip"
path = "tests/round_trip.rs"
[dependencies.object]
version = "0.37.1"
features = [
"std",
"read",
]
default-features = false
[dev-dependencies.cargo-binutils]
version = "0.3.6"
[dev-dependencies.object]
version = "0.37.1"
features = [
"write",
"xcoff",
]
default-features = false
[dev-dependencies.pretty_assertions]
version = "1.4.1"
[lints.rust.rust_2018_idioms]
level = "deny"
priority = 0

219
vendor/ar_archive_writer/LICENSE.txt vendored Normal file
View File

@@ -0,0 +1,219 @@
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.
--- LLVM Exceptions to the Apache 2.0 License ----
As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into an Object form of such source code, you
may redistribute such embedded portions in such Object form without complying
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
In addition, if you combine or link compiled forms of this Software with
software that is licensed under the GPLv2 ("Combined Software") and if a
court of competent jurisdiction determines that the patent provision (Section
3), the indemnity provision (Section 9) or other Section of the License
conflicts with the conditions of the GPLv2, you may retroactively and
prospectively choose to deem waived or otherwise exclude such Section(s) of
the License, but only in their entirety and only with respect to the Combined
Software.

17
vendor/ar_archive_writer/Readme.md vendored Normal file
View File

@@ -0,0 +1,17 @@
# A writer for object file ar archives
This is a Rust port of LLVM's archive writer (see [the LLVM Reference](reference/Readme.md)
for details) with the following options removed:
* Deterministic: always enabled.
* Symbol tables: always enabled.
## License
Licensed under Apache License v2.0 with LLVM Exceptions
([LICENSE.txt](LICENSE.txt) or https://llvm.org/LICENSE.txt)
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you shall be dual licensed as above, without any
additional terms or conditions.

View File

@@ -0,0 +1,327 @@
//===-- llvm/Support/Alignment.h - Useful alignment functions ---*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains types to represent alignments.
// They are instrumented to guarantee some invariants are preserved and prevent
// invalid manipulations.
//
// - Align represents an alignment in bytes, it is always set and always a valid
// power of two, its minimum value is 1 which means no alignment requirements.
//
// - MaybeAlign is an optional type, it may be undefined or set. When it's set
// you can get the underlying Align type by using the value() method.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_SUPPORT_ALIGNMENT_H_
#define LLVM_SUPPORT_ALIGNMENT_H_
#include "llvm/Support/MathExtras.h"
#include <cassert>
#include <optional>
#ifndef NDEBUG
#include <string>
#endif // NDEBUG
namespace llvm {
#define ALIGN_CHECK_ISPOSITIVE(decl) \
assert(decl > 0 && (#decl " should be defined"))
/// This struct is a compact representation of a valid (non-zero power of two)
/// alignment.
/// It is suitable for use as static global constants.
struct Align {
private:
uint8_t ShiftValue = 0; /// The log2 of the required alignment.
/// ShiftValue is less than 64 by construction.
friend struct MaybeAlign;
friend unsigned Log2(Align);
friend bool operator==(Align Lhs, Align Rhs);
friend bool operator!=(Align Lhs, Align Rhs);
friend bool operator<=(Align Lhs, Align Rhs);
friend bool operator>=(Align Lhs, Align Rhs);
friend bool operator<(Align Lhs, Align Rhs);
friend bool operator>(Align Lhs, Align Rhs);
friend unsigned encode(struct MaybeAlign A);
friend struct MaybeAlign decodeMaybeAlign(unsigned Value);
/// A trivial type to allow construction of constexpr Align.
/// This is currently needed to workaround a bug in GCC 5.3 which prevents
/// definition of constexpr assign operators.
/// https://stackoverflow.com/questions/46756288/explicitly-defaulted-function-cannot-be-declared-as-constexpr-because-the-implic
/// FIXME: Remove this, make all assign operators constexpr and introduce user
/// defined literals when we don't have to support GCC 5.3 anymore.
/// https://llvm.org/docs/GettingStarted.html#getting-a-modern-host-c-toolchain
struct LogValue {
uint8_t Log;
};
public:
/// Default is byte-aligned.
constexpr Align() = default;
/// Do not perform checks in case of copy/move construct/assign, because the
/// checks have been performed when building `Other`.
constexpr Align(const Align &Other) = default;
constexpr Align(Align &&Other) = default;
Align &operator=(const Align &Other) = default;
Align &operator=(Align &&Other) = default;
explicit Align(uint64_t Value) {
assert(Value > 0 && "Value must not be 0");
assert(llvm::isPowerOf2_64(Value) && "Alignment is not a power of 2");
ShiftValue = Log2_64(Value);
assert(ShiftValue < 64 && "Broken invariant");
}
/// This is a hole in the type system and should not be abused.
/// Needed to interact with C for instance.
uint64_t value() const { return uint64_t(1) << ShiftValue; }
// Returns the previous alignment.
Align previous() const {
assert(ShiftValue != 0 && "Undefined operation");
Align Out;
Out.ShiftValue = ShiftValue - 1;
return Out;
}
/// Allow constructions of constexpr Align.
template <size_t kValue> constexpr static Align Constant() {
return LogValue{static_cast<uint8_t>(CTLog2<kValue>())};
}
/// Allow constructions of constexpr Align from types.
/// Compile time equivalent to Align(alignof(T)).
template <typename T> constexpr static Align Of() {
return Constant<std::alignment_of_v<T>>();
}
/// Constexpr constructor from LogValue type.
constexpr Align(LogValue CA) : ShiftValue(CA.Log) {}
};
/// Treats the value 0 as a 1, so Align is always at least 1.
inline Align assumeAligned(uint64_t Value) {
return Value ? Align(Value) : Align();
}
/// This struct is a compact representation of a valid (power of two) or
/// undefined (0) alignment.
struct MaybeAlign : public std::optional<Align> {
private:
using UP = std::optional<Align>;
public:
/// Default is undefined.
MaybeAlign() = default;
/// Do not perform checks in case of copy/move construct/assign, because the
/// checks have been performed when building `Other`.
MaybeAlign(const MaybeAlign &Other) = default;
MaybeAlign &operator=(const MaybeAlign &Other) = default;
MaybeAlign(MaybeAlign &&Other) = default;
MaybeAlign &operator=(MaybeAlign &&Other) = default;
constexpr MaybeAlign(std::nullopt_t None) : UP(None) {}
constexpr MaybeAlign(Align Value) : UP(Value) {}
explicit MaybeAlign(uint64_t Value) {
assert((Value == 0 || llvm::isPowerOf2_64(Value)) &&
"Alignment is neither 0 nor a power of 2");
if (Value)
emplace(Value);
}
/// For convenience, returns a valid alignment or 1 if undefined.
Align valueOrOne() const { return value_or(Align()); }
};
/// Checks that SizeInBytes is a multiple of the alignment.
inline bool isAligned(Align Lhs, uint64_t SizeInBytes) {
return SizeInBytes % Lhs.value() == 0;
}
/// Checks that Addr is a multiple of the alignment.
inline bool isAddrAligned(Align Lhs, const void *Addr) {
return isAligned(Lhs, reinterpret_cast<uintptr_t>(Addr));
}
/// Returns a multiple of A needed to store `Size` bytes.
inline uint64_t alignTo(uint64_t Size, Align A) {
const uint64_t Value = A.value();
// The following line is equivalent to `(Size + Value - 1) / Value * Value`.
// The division followed by a multiplication can be thought of as a right
// shift followed by a left shift which zeros out the extra bits produced in
// the bump; `~(Value - 1)` is a mask where all those bits being zeroed out
// are just zero.
// Most compilers can generate this code but the pattern may be missed when
// multiple functions gets inlined.
return (Size + Value - 1) & ~(Value - 1U);
}
/// If non-zero \p Skew is specified, the return value will be a minimal integer
/// that is greater than or equal to \p Size and equal to \p A * N + \p Skew for
/// some integer N. If \p Skew is larger than \p A, its value is adjusted to '\p
/// Skew mod \p A'.
///
/// Examples:
/// \code
/// alignTo(5, Align(8), 7) = 7
/// alignTo(17, Align(8), 1) = 17
/// alignTo(~0LL, Align(8), 3) = 3
/// \endcode
inline uint64_t alignTo(uint64_t Size, Align A, uint64_t Skew) {
const uint64_t Value = A.value();
Skew %= Value;
return alignTo(Size - Skew, A) + Skew;
}
/// Aligns `Addr` to `Alignment` bytes, rounding up.
inline uintptr_t alignAddr(const void *Addr, Align Alignment) {
uintptr_t ArithAddr = reinterpret_cast<uintptr_t>(Addr);
assert(static_cast<uintptr_t>(ArithAddr + Alignment.value() - 1) >=
ArithAddr &&
"Overflow");
return alignTo(ArithAddr, Alignment);
}
/// Returns the offset to the next integer (mod 2**64) that is greater than
/// or equal to \p Value and is a multiple of \p Align.
inline uint64_t offsetToAlignment(uint64_t Value, Align Alignment) {
return alignTo(Value, Alignment) - Value;
}
/// Returns the necessary adjustment for aligning `Addr` to `Alignment`
/// bytes, rounding up.
inline uint64_t offsetToAlignedAddr(const void *Addr, Align Alignment) {
return offsetToAlignment(reinterpret_cast<uintptr_t>(Addr), Alignment);
}
/// Returns the log2 of the alignment.
inline unsigned Log2(Align A) { return A.ShiftValue; }
/// Returns the alignment that satisfies both alignments.
/// Same semantic as MinAlign.
inline Align commonAlignment(Align A, uint64_t Offset) {
return Align(MinAlign(A.value(), Offset));
}
/// Returns a representation of the alignment that encodes undefined as 0.
inline unsigned encode(MaybeAlign A) { return A ? A->ShiftValue + 1 : 0; }
/// Dual operation of the encode function above.
inline MaybeAlign decodeMaybeAlign(unsigned Value) {
if (Value == 0)
return MaybeAlign();
Align Out;
Out.ShiftValue = Value - 1;
return Out;
}
/// Returns a representation of the alignment, the encoded value is positive by
/// definition.
inline unsigned encode(Align A) { return encode(MaybeAlign(A)); }
/// Comparisons between Align and scalars. Rhs must be positive.
inline bool operator==(Align Lhs, uint64_t Rhs) {
ALIGN_CHECK_ISPOSITIVE(Rhs);
return Lhs.value() == Rhs;
}
inline bool operator!=(Align Lhs, uint64_t Rhs) {
ALIGN_CHECK_ISPOSITIVE(Rhs);
return Lhs.value() != Rhs;
}
inline bool operator<=(Align Lhs, uint64_t Rhs) {
ALIGN_CHECK_ISPOSITIVE(Rhs);
return Lhs.value() <= Rhs;
}
inline bool operator>=(Align Lhs, uint64_t Rhs) {
ALIGN_CHECK_ISPOSITIVE(Rhs);
return Lhs.value() >= Rhs;
}
inline bool operator<(Align Lhs, uint64_t Rhs) {
ALIGN_CHECK_ISPOSITIVE(Rhs);
return Lhs.value() < Rhs;
}
inline bool operator>(Align Lhs, uint64_t Rhs) {
ALIGN_CHECK_ISPOSITIVE(Rhs);
return Lhs.value() > Rhs;
}
/// Comparisons operators between Align.
inline bool operator==(Align Lhs, Align Rhs) {
return Lhs.ShiftValue == Rhs.ShiftValue;
}
inline bool operator!=(Align Lhs, Align Rhs) {
return Lhs.ShiftValue != Rhs.ShiftValue;
}
inline bool operator<=(Align Lhs, Align Rhs) {
return Lhs.ShiftValue <= Rhs.ShiftValue;
}
inline bool operator>=(Align Lhs, Align Rhs) {
return Lhs.ShiftValue >= Rhs.ShiftValue;
}
inline bool operator<(Align Lhs, Align Rhs) {
return Lhs.ShiftValue < Rhs.ShiftValue;
}
inline bool operator>(Align Lhs, Align Rhs) {
return Lhs.ShiftValue > Rhs.ShiftValue;
}
// Don't allow relational comparisons with MaybeAlign.
bool operator<=(Align Lhs, MaybeAlign Rhs) = delete;
bool operator>=(Align Lhs, MaybeAlign Rhs) = delete;
bool operator<(Align Lhs, MaybeAlign Rhs) = delete;
bool operator>(Align Lhs, MaybeAlign Rhs) = delete;
bool operator<=(MaybeAlign Lhs, Align Rhs) = delete;
bool operator>=(MaybeAlign Lhs, Align Rhs) = delete;
bool operator<(MaybeAlign Lhs, Align Rhs) = delete;
bool operator>(MaybeAlign Lhs, Align Rhs) = delete;
bool operator<=(MaybeAlign Lhs, MaybeAlign Rhs) = delete;
bool operator>=(MaybeAlign Lhs, MaybeAlign Rhs) = delete;
bool operator<(MaybeAlign Lhs, MaybeAlign Rhs) = delete;
bool operator>(MaybeAlign Lhs, MaybeAlign Rhs) = delete;
// Allow equality comparisons between Align and MaybeAlign.
inline bool operator==(MaybeAlign Lhs, Align Rhs) { return Lhs && *Lhs == Rhs; }
inline bool operator!=(MaybeAlign Lhs, Align Rhs) { return !(Lhs == Rhs); }
inline bool operator==(Align Lhs, MaybeAlign Rhs) { return Rhs == Lhs; }
inline bool operator!=(Align Lhs, MaybeAlign Rhs) { return !(Rhs == Lhs); }
// Allow equality comparisons with MaybeAlign.
inline bool operator==(MaybeAlign Lhs, MaybeAlign Rhs) {
return (Lhs && Rhs && (*Lhs == *Rhs)) || (!Lhs && !Rhs);
}
inline bool operator!=(MaybeAlign Lhs, MaybeAlign Rhs) { return !(Lhs == Rhs); }
// Allow equality comparisons with std::nullopt.
inline bool operator==(MaybeAlign Lhs, std::nullopt_t) { return !bool(Lhs); }
inline bool operator!=(MaybeAlign Lhs, std::nullopt_t) { return bool(Lhs); }
inline bool operator==(std::nullopt_t, MaybeAlign Rhs) { return !bool(Rhs); }
inline bool operator!=(std::nullopt_t, MaybeAlign Rhs) { return bool(Rhs); }
#ifndef NDEBUG
// For usage in LLVM_DEBUG macros.
inline std::string DebugStr(const Align &A) {
return std::to_string(A.value());
}
// For usage in LLVM_DEBUG macros.
inline std::string DebugStr(const MaybeAlign &MA) {
if (MA)
return std::to_string(MA->value());
return "None";
}
#endif // NDEBUG
#undef ALIGN_CHECK_ISPOSITIVE
} // namespace llvm
#endif // LLVM_SUPPORT_ALIGNMENT_H_

View File

@@ -0,0 +1,431 @@
//===- Archive.h - ar archive file format -----------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file declares the ar archive file format class.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_OBJECT_ARCHIVE_H
#define LLVM_OBJECT_ARCHIVE_H
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/fallible_iterator.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Object/Binary.h"
#include "llvm/Support/Chrono.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include <cassert>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
namespace llvm {
namespace object {
const char ArchiveMagic[] = "!<arch>\n";
const char ThinArchiveMagic[] = "!<thin>\n";
const char BigArchiveMagic[] = "<bigaf>\n";
class Archive;
class AbstractArchiveMemberHeader {
protected:
AbstractArchiveMemberHeader(const Archive *Parent) : Parent(Parent){};
public:
friend class Archive;
virtual std::unique_ptr<AbstractArchiveMemberHeader> clone() const = 0;
virtual ~AbstractArchiveMemberHeader() = default;
/// Get the name without looking up long names.
virtual Expected<StringRef> getRawName() const = 0;
virtual StringRef getRawAccessMode() const = 0;
virtual StringRef getRawLastModified() const = 0;
virtual StringRef getRawUID() const = 0;
virtual StringRef getRawGID() const = 0;
/// Get the name looking up long names.
virtual Expected<StringRef> getName(uint64_t Size) const = 0;
virtual Expected<uint64_t> getSize() const = 0;
virtual uint64_t getOffset() const = 0;
/// Get next file member location.
virtual Expected<const char *> getNextChildLoc() const = 0;
virtual Expected<bool> isThin() const = 0;
Expected<sys::fs::perms> getAccessMode() const;
Expected<sys::TimePoint<std::chrono::seconds>> getLastModified() const;
Expected<unsigned> getUID() const;
Expected<unsigned> getGID() const;
/// Returns the size in bytes of the format-defined member header of the
/// concrete archive type.
virtual uint64_t getSizeOf() const = 0;
const Archive *Parent;
};
template <typename T>
class CommonArchiveMemberHeader : public AbstractArchiveMemberHeader {
public:
CommonArchiveMemberHeader(const Archive *Parent, const T *RawHeaderPtr)
: AbstractArchiveMemberHeader(Parent), ArMemHdr(RawHeaderPtr){};
StringRef getRawAccessMode() const override;
StringRef getRawLastModified() const override;
StringRef getRawUID() const override;
StringRef getRawGID() const override;
uint64_t getOffset() const override;
uint64_t getSizeOf() const override { return sizeof(T); }
T const *ArMemHdr;
};
struct UnixArMemHdrType {
char Name[16];
char LastModified[12];
char UID[6];
char GID[6];
char AccessMode[8];
char Size[10]; ///< Size of data, not including header or padding.
char Terminator[2];
};
class ArchiveMemberHeader : public CommonArchiveMemberHeader<UnixArMemHdrType> {
public:
ArchiveMemberHeader(const Archive *Parent, const char *RawHeaderPtr,
uint64_t Size, Error *Err);
std::unique_ptr<AbstractArchiveMemberHeader> clone() const override {
return std::make_unique<ArchiveMemberHeader>(*this);
}
Expected<StringRef> getRawName() const override;
Expected<StringRef> getName(uint64_t Size) const override;
Expected<uint64_t> getSize() const override;
Expected<const char *> getNextChildLoc() const override;
Expected<bool> isThin() const override;
};
// File Member Header
struct BigArMemHdrType {
char Size[20]; // File member size in decimal
char NextOffset[20]; // Next member offset in decimal
char PrevOffset[20]; // Previous member offset in decimal
char LastModified[12];
char UID[12];
char GID[12];
char AccessMode[12];
char NameLen[4]; // File member name length in decimal
union {
char Name[2]; // Start of member name
char Terminator[2];
};
};
// Define file member header of AIX big archive.
class BigArchiveMemberHeader
: public CommonArchiveMemberHeader<BigArMemHdrType> {
public:
BigArchiveMemberHeader(Archive const *Parent, const char *RawHeaderPtr,
uint64_t Size, Error *Err);
std::unique_ptr<AbstractArchiveMemberHeader> clone() const override {
return std::make_unique<BigArchiveMemberHeader>(*this);
}
Expected<StringRef> getRawName() const override;
Expected<uint64_t> getRawNameSize() const;
Expected<StringRef> getName(uint64_t Size) const override;
Expected<uint64_t> getSize() const override;
Expected<const char *> getNextChildLoc() const override;
Expected<uint64_t> getNextOffset() const;
Expected<bool> isThin() const override { return false; }
};
class Archive : public Binary {
virtual void anchor();
public:
class Child {
friend Archive;
friend AbstractArchiveMemberHeader;
const Archive *Parent;
std::unique_ptr<AbstractArchiveMemberHeader> Header;
/// Includes header but not padding byte.
StringRef Data;
/// Offset from Data to the start of the file.
uint16_t StartOfFile;
Expected<bool> isThinMember() const;
public:
Child(const Archive *Parent, const char *Start, Error *Err);
Child(const Archive *Parent, StringRef Data, uint16_t StartOfFile);
Child(const Child &C)
: Parent(C.Parent), Data(C.Data), StartOfFile(C.StartOfFile) {
if (C.Header)
Header = C.Header->clone();
}
Child(Child &&C) {
Parent = std::move(C.Parent);
Header = std::move(C.Header);
Data = C.Data;
StartOfFile = C.StartOfFile;
}
Child &operator=(Child &&C) noexcept {
if (&C == this)
return *this;
Parent = std::move(C.Parent);
Header = std::move(C.Header);
Data = C.Data;
StartOfFile = C.StartOfFile;
return *this;
}
Child &operator=(const Child &C) {
if (&C == this)
return *this;
Parent = C.Parent;
if (C.Header)
Header = C.Header->clone();
Data = C.Data;
StartOfFile = C.StartOfFile;
return *this;
}
bool operator==(const Child &other) const {
assert(!Parent || !other.Parent || Parent == other.Parent);
return Data.begin() == other.Data.begin();
}
const Archive *getParent() const { return Parent; }
Expected<Child> getNext() const;
Expected<StringRef> getName() const;
Expected<std::string> getFullName() const;
Expected<StringRef> getRawName() const { return Header->getRawName(); }
Expected<sys::TimePoint<std::chrono::seconds>> getLastModified() const {
return Header->getLastModified();
}
StringRef getRawLastModified() const {
return Header->getRawLastModified();
}
Expected<unsigned> getUID() const { return Header->getUID(); }
Expected<unsigned> getGID() const { return Header->getGID(); }
Expected<sys::fs::perms> getAccessMode() const {
return Header->getAccessMode();
}
/// \return the size of the archive member without the header or padding.
Expected<uint64_t> getSize() const;
/// \return the size in the archive header for this member.
Expected<uint64_t> getRawSize() const;
Expected<StringRef> getBuffer() const;
uint64_t getChildOffset() const;
uint64_t getDataOffset() const { return getChildOffset() + StartOfFile; }
Expected<MemoryBufferRef> getMemoryBufferRef() const;
Expected<std::unique_ptr<Binary>>
getAsBinary(LLVMContext *Context = nullptr) const;
};
class ChildFallibleIterator {
Child C;
public:
ChildFallibleIterator() : C(Child(nullptr, nullptr, nullptr)) {}
ChildFallibleIterator(const Child &C) : C(C) {}
const Child *operator->() const { return &C; }
const Child &operator*() const { return C; }
bool operator==(const ChildFallibleIterator &other) const {
// Ignore errors here: If an error occurred during increment then getNext
// will have been set to child_end(), and the following comparison should
// do the right thing.
return C == other.C;
}
bool operator!=(const ChildFallibleIterator &other) const {
return !(*this == other);
}
Error inc() {
auto NextChild = C.getNext();
if (!NextChild)
return NextChild.takeError();
C = std::move(*NextChild);
return Error::success();
}
};
using child_iterator = fallible_iterator<ChildFallibleIterator>;
class Symbol {
const Archive *Parent;
uint32_t SymbolIndex;
uint32_t StringIndex; // Extra index to the string.
public:
Symbol(const Archive *p, uint32_t symi, uint32_t stri)
: Parent(p), SymbolIndex(symi), StringIndex(stri) {}
bool operator==(const Symbol &other) const {
return (Parent == other.Parent) && (SymbolIndex == other.SymbolIndex);
}
StringRef getName() const;
Expected<Child> getMember() const;
Symbol getNext() const;
bool isECSymbol() const;
};
class symbol_iterator {
Symbol symbol;
public:
symbol_iterator(const Symbol &s) : symbol(s) {}
const Symbol *operator->() const { return &symbol; }
const Symbol &operator*() const { return symbol; }
bool operator==(const symbol_iterator &other) const {
return symbol == other.symbol;
}
bool operator!=(const symbol_iterator &other) const {
return !(*this == other);
}
symbol_iterator &operator++() { // Preincrement
symbol = symbol.getNext();
return *this;
}
};
Archive(MemoryBufferRef Source, Error &Err);
static Expected<std::unique_ptr<Archive>> create(MemoryBufferRef Source);
/// Size field is 10 decimal digits long
static const uint64_t MaxMemberSize = 9999999999;
enum Kind { K_GNU, K_GNU64, K_BSD, K_DARWIN, K_DARWIN64, K_COFF, K_AIXBIG };
Kind kind() const { return (Kind)Format; }
bool isThin() const { return IsThin; }
static object::Archive::Kind getDefaultKind();
static object::Archive::Kind getDefaultKindForTriple(const Triple &T);
child_iterator child_begin(Error &Err, bool SkipInternal = true) const;
child_iterator child_end() const;
iterator_range<child_iterator> children(Error &Err,
bool SkipInternal = true) const {
return make_range(child_begin(Err, SkipInternal), child_end());
}
symbol_iterator symbol_begin() const;
symbol_iterator symbol_end() const;
iterator_range<symbol_iterator> symbols() const {
return make_range(symbol_begin(), symbol_end());
}
Expected<iterator_range<symbol_iterator>> ec_symbols() const;
static bool classof(Binary const *v) { return v->isArchive(); }
// check if a symbol is in the archive
Expected<std::optional<Child>> findSym(StringRef name) const;
virtual bool isEmpty() const;
bool hasSymbolTable() const;
StringRef getSymbolTable() const { return SymbolTable; }
StringRef getStringTable() const { return StringTable; }
uint32_t getNumberOfSymbols() const;
uint32_t getNumberOfECSymbols() const;
virtual uint64_t getFirstChildOffset() const { return getArchiveMagicLen(); }
std::vector<std::unique_ptr<MemoryBuffer>> takeThinBuffers() {
return std::move(ThinBuffers);
}
std::unique_ptr<AbstractArchiveMemberHeader>
createArchiveMemberHeader(const char *RawHeaderPtr, uint64_t Size,
Error *Err) const;
protected:
uint64_t getArchiveMagicLen() const;
void setFirstRegular(const Child &C);
StringRef SymbolTable;
StringRef ECSymbolTable;
StringRef StringTable;
private:
StringRef FirstRegularData;
uint16_t FirstRegularStartOfFile = -1;
unsigned Format : 3;
unsigned IsThin : 1;
mutable std::vector<std::unique_ptr<MemoryBuffer>> ThinBuffers;
};
class BigArchive : public Archive {
public:
/// Fixed-Length Header.
struct FixLenHdr {
char Magic[sizeof(BigArchiveMagic) - 1]; ///< Big archive magic string.
char MemOffset[20]; ///< Offset to member table.
char GlobSymOffset[20]; ///< Offset to global symbol table.
char
GlobSym64Offset[20]; ///< Offset global symbol table for 64-bit objects.
char FirstChildOffset[20]; ///< Offset to first archive member.
char LastChildOffset[20]; ///< Offset to last archive member.
char FreeOffset[20]; ///< Offset to first mem on free list.
};
const FixLenHdr *ArFixLenHdr;
uint64_t FirstChildOffset = 0;
uint64_t LastChildOffset = 0;
std::string MergedGlobalSymtabBuf;
bool Has32BitGlobalSymtab = false;
bool Has64BitGlobalSymtab = false;
public:
BigArchive(MemoryBufferRef Source, Error &Err);
uint64_t getFirstChildOffset() const override { return FirstChildOffset; }
uint64_t getLastChildOffset() const { return LastChildOffset; }
bool isEmpty() const override { return getFirstChildOffset() == 0; }
bool has32BitGlobalSymtab() { return Has32BitGlobalSymtab; }
bool has64BitGlobalSymtab() { return Has64BitGlobalSymtab; }
};
} // end namespace object
} // end namespace llvm
#endif // LLVM_OBJECT_ARCHIVE_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,77 @@
//===- ArchiveWriter.h - ar archive file format writer ----------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Declares the writeArchive function for writing an archive file.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_OBJECT_ARCHIVEWRITER_H
#define LLVM_OBJECT_ARCHIVEWRITER_H
#include "llvm/Object/Archive.h"
namespace llvm {
struct NewArchiveMember {
std::unique_ptr<MemoryBuffer> Buf;
StringRef MemberName;
sys::TimePoint<std::chrono::seconds> ModTime;
unsigned UID = 0, GID = 0, Perms = 0644;
NewArchiveMember() = default;
NewArchiveMember(MemoryBufferRef BufRef);
// Detect the archive format from the object or bitcode file. This helps
// assume the archive format when creating or editing archives in the case
// one isn't explicitly set.
object::Archive::Kind detectKindFromObject() const;
static Expected<NewArchiveMember>
getOldMember(const object::Archive::Child &OldMember, bool Deterministic);
static Expected<NewArchiveMember> getFile(StringRef FileName,
bool Deterministic);
};
Expected<std::string> computeArchiveRelativePath(StringRef From, StringRef To);
enum class SymtabWritingMode {
NoSymtab, // Do not write symbol table.
NormalSymtab, // Write symbol table. For the Big Archive format, write both
// 32-bit and 64-bit symbol tables.
BigArchive32, // Only write the 32-bit symbol table.
BigArchive64 // Only write the 64-bit symbol table.
};
void warnToStderr(Error Err);
// Write an archive directly to an output stream.
Error writeArchiveToStream(raw_ostream &Out,
ArrayRef<NewArchiveMember> NewMembers,
SymtabWritingMode WriteSymtab,
object::Archive::Kind Kind, bool Deterministic,
bool Thin, std::optional<bool> IsEC = std::nullopt,
function_ref<void(Error)> Warn = warnToStderr);
Error writeArchive(StringRef ArcName, ArrayRef<NewArchiveMember> NewMembers,
SymtabWritingMode WriteSymtab, object::Archive::Kind Kind,
bool Deterministic, bool Thin,
std::unique_ptr<MemoryBuffer> OldArchiveBuf = nullptr,
std::optional<bool> IsEC = std::nullopt,
function_ref<void(Error)> Warn = warnToStderr);
// writeArchiveToBuffer is similar to writeArchive but returns the Archive in a
// buffer instead of writing it out to a file.
Expected<std::unique_ptr<MemoryBuffer>>
writeArchiveToBuffer(ArrayRef<NewArchiveMember> NewMembers,
SymtabWritingMode WriteSymtab, object::Archive::Kind Kind,
bool Deterministic, bool Thin,
function_ref<void(Error)> Warn = warnToStderr);
}
#endif

View File

@@ -0,0 +1,859 @@
//===-- llvm/BinaryFormat/COFF.h --------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains an definitions used in Windows COFF Files.
//
// Structures and enums defined within this file where created using
// information from Microsoft's publicly available PE/COFF format document:
//
// Microsoft Portable Executable and Common Object File Format Specification
// Revision 8.1 - February 15, 2008
//
// As of 5/2/2010, hosted by Microsoft at:
// http://www.microsoft.com/whdc/system/platform/firmware/pecoff.mspx
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_BINARYFORMAT_COFF_H
#define LLVM_BINARYFORMAT_COFF_H
#include "llvm/Support/DataTypes.h"
#include <cassert>
namespace llvm {
namespace COFF {
// The maximum number of sections that a COFF object can have (inclusive).
const int32_t MaxNumberOfSections16 = 65279;
// The PE signature bytes that follows the DOS stub header.
static const char PEMagic[] = {'P', 'E', '\0', '\0'};
static const char BigObjMagic[] = {
'\xc7', '\xa1', '\xba', '\xd1', '\xee', '\xba', '\xa9', '\x4b',
'\xaf', '\x20', '\xfa', '\xf6', '\x6a', '\xa4', '\xdc', '\xb8',
};
static const char ClGlObjMagic[] = {
'\x38', '\xfe', '\xb3', '\x0c', '\xa5', '\xd9', '\xab', '\x4d',
'\xac', '\x9b', '\xd6', '\xb6', '\x22', '\x26', '\x53', '\xc2',
};
// The signature bytes that start a .res file.
static const char WinResMagic[] = {
'\x00', '\x00', '\x00', '\x00', '\x20', '\x00', '\x00', '\x00',
'\xff', '\xff', '\x00', '\x00', '\xff', '\xff', '\x00', '\x00',
};
// Sizes in bytes of various things in the COFF format.
enum {
Header16Size = 20,
Header32Size = 56,
NameSize = 8,
Symbol16Size = 18,
Symbol32Size = 20,
SectionSize = 40,
RelocationSize = 10
};
struct header {
uint16_t Machine;
int32_t NumberOfSections;
uint32_t TimeDateStamp;
uint32_t PointerToSymbolTable;
uint32_t NumberOfSymbols;
uint16_t SizeOfOptionalHeader;
uint16_t Characteristics;
};
struct BigObjHeader {
enum : uint16_t { MinBigObjectVersion = 2 };
uint16_t Sig1; ///< Must be IMAGE_FILE_MACHINE_UNKNOWN (0).
uint16_t Sig2; ///< Must be 0xFFFF.
uint16_t Version;
uint16_t Machine;
uint32_t TimeDateStamp;
uint8_t UUID[16];
uint32_t unused1;
uint32_t unused2;
uint32_t unused3;
uint32_t unused4;
uint32_t NumberOfSections;
uint32_t PointerToSymbolTable;
uint32_t NumberOfSymbols;
};
enum MachineTypes : unsigned {
MT_Invalid = 0xffff,
IMAGE_FILE_MACHINE_UNKNOWN = 0x0,
IMAGE_FILE_MACHINE_AM33 = 0x1D3,
IMAGE_FILE_MACHINE_AMD64 = 0x8664,
IMAGE_FILE_MACHINE_ARM = 0x1C0,
IMAGE_FILE_MACHINE_ARMNT = 0x1C4,
IMAGE_FILE_MACHINE_ARM64 = 0xAA64,
IMAGE_FILE_MACHINE_ARM64EC = 0xA641,
IMAGE_FILE_MACHINE_ARM64X = 0xA64E,
IMAGE_FILE_MACHINE_EBC = 0xEBC,
IMAGE_FILE_MACHINE_I386 = 0x14C,
IMAGE_FILE_MACHINE_IA64 = 0x200,
IMAGE_FILE_MACHINE_M32R = 0x9041,
IMAGE_FILE_MACHINE_MIPS16 = 0x266,
IMAGE_FILE_MACHINE_MIPSFPU = 0x366,
IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466,
IMAGE_FILE_MACHINE_POWERPC = 0x1F0,
IMAGE_FILE_MACHINE_POWERPCFP = 0x1F1,
IMAGE_FILE_MACHINE_R4000 = 0x166,
IMAGE_FILE_MACHINE_RISCV32 = 0x5032,
IMAGE_FILE_MACHINE_RISCV64 = 0x5064,
IMAGE_FILE_MACHINE_RISCV128 = 0x5128,
IMAGE_FILE_MACHINE_SH3 = 0x1A2,
IMAGE_FILE_MACHINE_SH3DSP = 0x1A3,
IMAGE_FILE_MACHINE_SH4 = 0x1A6,
IMAGE_FILE_MACHINE_SH5 = 0x1A8,
IMAGE_FILE_MACHINE_THUMB = 0x1C2,
IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169
};
template <typename T> bool isArm64EC(T Machine) {
return Machine == IMAGE_FILE_MACHINE_ARM64EC ||
Machine == IMAGE_FILE_MACHINE_ARM64X;
}
template <typename T> bool isAnyArm64(T Machine) {
return Machine == IMAGE_FILE_MACHINE_ARM64 || isArm64EC(Machine);
}
template <typename T> bool is64Bit(T Machine) {
return Machine == IMAGE_FILE_MACHINE_AMD64 || isAnyArm64(Machine);
}
enum Characteristics : unsigned {
C_Invalid = 0,
/// The file does not contain base relocations and must be loaded at its
/// preferred base. If this cannot be done, the loader will error.
IMAGE_FILE_RELOCS_STRIPPED = 0x0001,
/// The file is valid and can be run.
IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002,
/// COFF line numbers have been stripped. This is deprecated and should be
/// 0.
IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004,
/// COFF symbol table entries for local symbols have been removed. This is
/// deprecated and should be 0.
IMAGE_FILE_LOCAL_SYMS_STRIPPED = 0x0008,
/// Aggressively trim working set. This is deprecated and must be 0.
IMAGE_FILE_AGGRESSIVE_WS_TRIM = 0x0010,
/// Image can handle > 2GiB addresses.
IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020,
/// Little endian: the LSB precedes the MSB in memory. This is deprecated
/// and should be 0.
IMAGE_FILE_BYTES_REVERSED_LO = 0x0080,
/// Machine is based on a 32bit word architecture.
IMAGE_FILE_32BIT_MACHINE = 0x0100,
/// Debugging info has been removed.
IMAGE_FILE_DEBUG_STRIPPED = 0x0200,
/// If the image is on removable media, fully load it and copy it to swap.
IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 0x0400,
/// If the image is on network media, fully load it and copy it to swap.
IMAGE_FILE_NET_RUN_FROM_SWAP = 0x0800,
/// The image file is a system file, not a user program.
IMAGE_FILE_SYSTEM = 0x1000,
/// The image file is a DLL.
IMAGE_FILE_DLL = 0x2000,
/// This file should only be run on a uniprocessor machine.
IMAGE_FILE_UP_SYSTEM_ONLY = 0x4000,
/// Big endian: the MSB precedes the LSB in memory. This is deprecated
/// and should be 0.
IMAGE_FILE_BYTES_REVERSED_HI = 0x8000
};
enum ResourceTypeID : unsigned {
RID_Cursor = 1,
RID_Bitmap = 2,
RID_Icon = 3,
RID_Menu = 4,
RID_Dialog = 5,
RID_String = 6,
RID_FontDir = 7,
RID_Font = 8,
RID_Accelerator = 9,
RID_RCData = 10,
RID_MessageTable = 11,
RID_Group_Cursor = 12,
RID_Group_Icon = 14,
RID_Version = 16,
RID_DLGInclude = 17,
RID_PlugPlay = 19,
RID_VXD = 20,
RID_AniCursor = 21,
RID_AniIcon = 22,
RID_HTML = 23,
RID_Manifest = 24,
};
struct symbol {
char Name[NameSize];
uint32_t Value;
int32_t SectionNumber;
uint16_t Type;
uint8_t StorageClass;
uint8_t NumberOfAuxSymbols;
};
enum SymbolSectionNumber : int32_t {
IMAGE_SYM_DEBUG = -2,
IMAGE_SYM_ABSOLUTE = -1,
IMAGE_SYM_UNDEFINED = 0
};
/// Storage class tells where and what the symbol represents
enum SymbolStorageClass {
SSC_Invalid = 0xff,
IMAGE_SYM_CLASS_END_OF_FUNCTION = -1, ///< Physical end of function
IMAGE_SYM_CLASS_NULL = 0, ///< No symbol
IMAGE_SYM_CLASS_AUTOMATIC = 1, ///< Stack variable
IMAGE_SYM_CLASS_EXTERNAL = 2, ///< External symbol
IMAGE_SYM_CLASS_STATIC = 3, ///< Static
IMAGE_SYM_CLASS_REGISTER = 4, ///< Register variable
IMAGE_SYM_CLASS_EXTERNAL_DEF = 5, ///< External definition
IMAGE_SYM_CLASS_LABEL = 6, ///< Label
IMAGE_SYM_CLASS_UNDEFINED_LABEL = 7, ///< Undefined label
IMAGE_SYM_CLASS_MEMBER_OF_STRUCT = 8, ///< Member of structure
IMAGE_SYM_CLASS_ARGUMENT = 9, ///< Function argument
IMAGE_SYM_CLASS_STRUCT_TAG = 10, ///< Structure tag
IMAGE_SYM_CLASS_MEMBER_OF_UNION = 11, ///< Member of union
IMAGE_SYM_CLASS_UNION_TAG = 12, ///< Union tag
IMAGE_SYM_CLASS_TYPE_DEFINITION = 13, ///< Type definition
IMAGE_SYM_CLASS_UNDEFINED_STATIC = 14, ///< Undefined static
IMAGE_SYM_CLASS_ENUM_TAG = 15, ///< Enumeration tag
IMAGE_SYM_CLASS_MEMBER_OF_ENUM = 16, ///< Member of enumeration
IMAGE_SYM_CLASS_REGISTER_PARAM = 17, ///< Register parameter
IMAGE_SYM_CLASS_BIT_FIELD = 18, ///< Bit field
/// ".bb" or ".eb" - beginning or end of block
IMAGE_SYM_CLASS_BLOCK = 100,
/// ".bf" or ".ef" - beginning or end of function
IMAGE_SYM_CLASS_FUNCTION = 101,
IMAGE_SYM_CLASS_END_OF_STRUCT = 102, ///< End of structure
IMAGE_SYM_CLASS_FILE = 103, ///< File name
/// Line number, reformatted as symbol
IMAGE_SYM_CLASS_SECTION = 104,
IMAGE_SYM_CLASS_WEAK_EXTERNAL = 105, ///< Duplicate tag
/// External symbol in dmert public lib
IMAGE_SYM_CLASS_CLR_TOKEN = 107
};
enum SymbolBaseType : unsigned {
IMAGE_SYM_TYPE_NULL = 0, ///< No type information or unknown base type.
IMAGE_SYM_TYPE_VOID = 1, ///< Used with void pointers and functions.
IMAGE_SYM_TYPE_CHAR = 2, ///< A character (signed byte).
IMAGE_SYM_TYPE_SHORT = 3, ///< A 2-byte signed integer.
IMAGE_SYM_TYPE_INT = 4, ///< A natural integer type on the target.
IMAGE_SYM_TYPE_LONG = 5, ///< A 4-byte signed integer.
IMAGE_SYM_TYPE_FLOAT = 6, ///< A 4-byte floating-point number.
IMAGE_SYM_TYPE_DOUBLE = 7, ///< An 8-byte floating-point number.
IMAGE_SYM_TYPE_STRUCT = 8, ///< A structure.
IMAGE_SYM_TYPE_UNION = 9, ///< An union.
IMAGE_SYM_TYPE_ENUM = 10, ///< An enumerated type.
IMAGE_SYM_TYPE_MOE = 11, ///< A member of enumeration (a specific value).
IMAGE_SYM_TYPE_BYTE = 12, ///< A byte; unsigned 1-byte integer.
IMAGE_SYM_TYPE_WORD = 13, ///< A word; unsigned 2-byte integer.
IMAGE_SYM_TYPE_UINT = 14, ///< An unsigned integer of natural size.
IMAGE_SYM_TYPE_DWORD = 15 ///< An unsigned 4-byte integer.
};
enum SymbolComplexType : unsigned {
IMAGE_SYM_DTYPE_NULL = 0, ///< No complex type; simple scalar variable.
IMAGE_SYM_DTYPE_POINTER = 1, ///< A pointer to base type.
IMAGE_SYM_DTYPE_FUNCTION = 2, ///< A function that returns a base type.
IMAGE_SYM_DTYPE_ARRAY = 3, ///< An array of base type.
/// Type is formed as (base + (derived << SCT_COMPLEX_TYPE_SHIFT))
SCT_COMPLEX_TYPE_SHIFT = 4
};
enum AuxSymbolType { IMAGE_AUX_SYMBOL_TYPE_TOKEN_DEF = 1 };
struct section {
char Name[NameSize];
uint32_t VirtualSize;
uint32_t VirtualAddress;
uint32_t SizeOfRawData;
uint32_t PointerToRawData;
uint32_t PointerToRelocations;
uint32_t PointerToLineNumbers;
uint16_t NumberOfRelocations;
uint16_t NumberOfLineNumbers;
uint32_t Characteristics;
};
enum SectionCharacteristics : uint32_t {
SC_Invalid = 0xffffffff,
IMAGE_SCN_TYPE_NOLOAD = 0x00000002,
IMAGE_SCN_TYPE_NO_PAD = 0x00000008,
IMAGE_SCN_CNT_CODE = 0x00000020,
IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040,
IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080,
IMAGE_SCN_LNK_OTHER = 0x00000100,
IMAGE_SCN_LNK_INFO = 0x00000200,
IMAGE_SCN_LNK_REMOVE = 0x00000800,
IMAGE_SCN_LNK_COMDAT = 0x00001000,
IMAGE_SCN_GPREL = 0x00008000,
IMAGE_SCN_MEM_PURGEABLE = 0x00020000,
IMAGE_SCN_MEM_16BIT = 0x00020000,
IMAGE_SCN_MEM_LOCKED = 0x00040000,
IMAGE_SCN_MEM_PRELOAD = 0x00080000,
IMAGE_SCN_ALIGN_1BYTES = 0x00100000,
IMAGE_SCN_ALIGN_2BYTES = 0x00200000,
IMAGE_SCN_ALIGN_4BYTES = 0x00300000,
IMAGE_SCN_ALIGN_8BYTES = 0x00400000,
IMAGE_SCN_ALIGN_16BYTES = 0x00500000,
IMAGE_SCN_ALIGN_32BYTES = 0x00600000,
IMAGE_SCN_ALIGN_64BYTES = 0x00700000,
IMAGE_SCN_ALIGN_128BYTES = 0x00800000,
IMAGE_SCN_ALIGN_256BYTES = 0x00900000,
IMAGE_SCN_ALIGN_512BYTES = 0x00A00000,
IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000,
IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000,
IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000,
IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000,
IMAGE_SCN_ALIGN_MASK = 0x00F00000,
IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000,
IMAGE_SCN_MEM_DISCARDABLE = 0x02000000,
IMAGE_SCN_MEM_NOT_CACHED = 0x04000000,
IMAGE_SCN_MEM_NOT_PAGED = 0x08000000,
IMAGE_SCN_MEM_SHARED = 0x10000000,
IMAGE_SCN_MEM_EXECUTE = 0x20000000,
IMAGE_SCN_MEM_READ = 0x40000000,
IMAGE_SCN_MEM_WRITE = 0x80000000
};
struct relocation {
uint32_t VirtualAddress;
uint32_t SymbolTableIndex;
uint16_t Type;
};
enum RelocationTypeI386 : unsigned {
IMAGE_REL_I386_ABSOLUTE = 0x0000,
IMAGE_REL_I386_DIR16 = 0x0001,
IMAGE_REL_I386_REL16 = 0x0002,
IMAGE_REL_I386_DIR32 = 0x0006,
IMAGE_REL_I386_DIR32NB = 0x0007,
IMAGE_REL_I386_SEG12 = 0x0009,
IMAGE_REL_I386_SECTION = 0x000A,
IMAGE_REL_I386_SECREL = 0x000B,
IMAGE_REL_I386_TOKEN = 0x000C,
IMAGE_REL_I386_SECREL7 = 0x000D,
IMAGE_REL_I386_REL32 = 0x0014
};
enum RelocationTypeAMD64 : unsigned {
IMAGE_REL_AMD64_ABSOLUTE = 0x0000,
IMAGE_REL_AMD64_ADDR64 = 0x0001,
IMAGE_REL_AMD64_ADDR32 = 0x0002,
IMAGE_REL_AMD64_ADDR32NB = 0x0003,
IMAGE_REL_AMD64_REL32 = 0x0004,
IMAGE_REL_AMD64_REL32_1 = 0x0005,
IMAGE_REL_AMD64_REL32_2 = 0x0006,
IMAGE_REL_AMD64_REL32_3 = 0x0007,
IMAGE_REL_AMD64_REL32_4 = 0x0008,
IMAGE_REL_AMD64_REL32_5 = 0x0009,
IMAGE_REL_AMD64_SECTION = 0x000A,
IMAGE_REL_AMD64_SECREL = 0x000B,
IMAGE_REL_AMD64_SECREL7 = 0x000C,
IMAGE_REL_AMD64_TOKEN = 0x000D,
IMAGE_REL_AMD64_SREL32 = 0x000E,
IMAGE_REL_AMD64_PAIR = 0x000F,
IMAGE_REL_AMD64_SSPAN32 = 0x0010
};
enum RelocationTypesARM : unsigned {
IMAGE_REL_ARM_ABSOLUTE = 0x0000,
IMAGE_REL_ARM_ADDR32 = 0x0001,
IMAGE_REL_ARM_ADDR32NB = 0x0002,
IMAGE_REL_ARM_BRANCH24 = 0x0003,
IMAGE_REL_ARM_BRANCH11 = 0x0004,
IMAGE_REL_ARM_TOKEN = 0x0005,
IMAGE_REL_ARM_BLX24 = 0x0008,
IMAGE_REL_ARM_BLX11 = 0x0009,
IMAGE_REL_ARM_REL32 = 0x000A,
IMAGE_REL_ARM_SECTION = 0x000E,
IMAGE_REL_ARM_SECREL = 0x000F,
IMAGE_REL_ARM_MOV32A = 0x0010,
IMAGE_REL_ARM_MOV32T = 0x0011,
IMAGE_REL_ARM_BRANCH20T = 0x0012,
IMAGE_REL_ARM_BRANCH24T = 0x0014,
IMAGE_REL_ARM_BLX23T = 0x0015,
IMAGE_REL_ARM_PAIR = 0x0016,
};
enum RelocationTypesARM64 : unsigned {
IMAGE_REL_ARM64_ABSOLUTE = 0x0000,
IMAGE_REL_ARM64_ADDR32 = 0x0001,
IMAGE_REL_ARM64_ADDR32NB = 0x0002,
IMAGE_REL_ARM64_BRANCH26 = 0x0003,
IMAGE_REL_ARM64_PAGEBASE_REL21 = 0x0004,
IMAGE_REL_ARM64_REL21 = 0x0005,
IMAGE_REL_ARM64_PAGEOFFSET_12A = 0x0006,
IMAGE_REL_ARM64_PAGEOFFSET_12L = 0x0007,
IMAGE_REL_ARM64_SECREL = 0x0008,
IMAGE_REL_ARM64_SECREL_LOW12A = 0x0009,
IMAGE_REL_ARM64_SECREL_HIGH12A = 0x000A,
IMAGE_REL_ARM64_SECREL_LOW12L = 0x000B,
IMAGE_REL_ARM64_TOKEN = 0x000C,
IMAGE_REL_ARM64_SECTION = 0x000D,
IMAGE_REL_ARM64_ADDR64 = 0x000E,
IMAGE_REL_ARM64_BRANCH19 = 0x000F,
IMAGE_REL_ARM64_BRANCH14 = 0x0010,
IMAGE_REL_ARM64_REL32 = 0x0011,
};
enum RelocationTypesMips : unsigned {
IMAGE_REL_MIPS_ABSOLUTE = 0x0000,
IMAGE_REL_MIPS_REFHALF = 0x0001,
IMAGE_REL_MIPS_REFWORD = 0x0002,
IMAGE_REL_MIPS_JMPADDR = 0x0003,
IMAGE_REL_MIPS_REFHI = 0x0004,
IMAGE_REL_MIPS_REFLO = 0x0005,
IMAGE_REL_MIPS_GPREL = 0x0006,
IMAGE_REL_MIPS_LITERAL = 0x0007,
IMAGE_REL_MIPS_SECTION = 0x000A,
IMAGE_REL_MIPS_SECREL = 0x000B,
IMAGE_REL_MIPS_SECRELLO = 0x000C,
IMAGE_REL_MIPS_SECRELHI = 0x000D,
IMAGE_REL_MIPS_JMPADDR16 = 0x0010,
IMAGE_REL_MIPS_REFWORDNB = 0x0022,
IMAGE_REL_MIPS_PAIR = 0x0025,
};
enum DynamicRelocationType : unsigned {
IMAGE_DYNAMIC_RELOCATION_GUARD_RF_PROLOGUE = 1,
IMAGE_DYNAMIC_RELOCATION_GUARD_RF_EPILOGUE = 2,
IMAGE_DYNAMIC_RELOCATION_GUARD_IMPORT_CONTROL_TRANSFER = 3,
IMAGE_DYNAMIC_RELOCATION_GUARD_INDIR_CONTROL_TRANSFER = 4,
IMAGE_DYNAMIC_RELOCATION_GUARD_SWITCHTABLE_BRANCH = 5,
IMAGE_DYNAMIC_RELOCATION_ARM64X = 6,
};
enum Arm64XFixupType : uint8_t {
IMAGE_DVRT_ARM64X_FIXUP_TYPE_ZEROFILL = 0,
IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE = 1,
IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA = 2,
};
enum COMDATType : uint8_t {
IMAGE_COMDAT_SELECT_NODUPLICATES = 1,
IMAGE_COMDAT_SELECT_ANY,
IMAGE_COMDAT_SELECT_SAME_SIZE,
IMAGE_COMDAT_SELECT_EXACT_MATCH,
IMAGE_COMDAT_SELECT_ASSOCIATIVE,
IMAGE_COMDAT_SELECT_LARGEST,
IMAGE_COMDAT_SELECT_NEWEST
};
// Auxiliary Symbol Formats
struct AuxiliaryFunctionDefinition {
uint32_t TagIndex;
uint32_t TotalSize;
uint32_t PointerToLinenumber;
uint32_t PointerToNextFunction;
char unused[2];
};
struct AuxiliarybfAndefSymbol {
uint8_t unused1[4];
uint16_t Linenumber;
uint8_t unused2[6];
uint32_t PointerToNextFunction;
uint8_t unused3[2];
};
struct AuxiliaryWeakExternal {
uint32_t TagIndex;
uint32_t Characteristics;
uint8_t unused[10];
};
enum WeakExternalCharacteristics : unsigned {
IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY = 1,
IMAGE_WEAK_EXTERN_SEARCH_LIBRARY = 2,
IMAGE_WEAK_EXTERN_SEARCH_ALIAS = 3,
IMAGE_WEAK_EXTERN_ANTI_DEPENDENCY = 4
};
struct AuxiliarySectionDefinition {
uint32_t Length;
uint16_t NumberOfRelocations;
uint16_t NumberOfLinenumbers;
uint32_t CheckSum;
uint32_t Number;
uint8_t Selection;
char unused;
};
struct AuxiliaryCLRToken {
uint8_t AuxType;
uint8_t unused1;
uint32_t SymbolTableIndex;
char unused2[12];
};
union Auxiliary {
AuxiliaryFunctionDefinition FunctionDefinition;
AuxiliarybfAndefSymbol bfAndefSymbol;
AuxiliaryWeakExternal WeakExternal;
AuxiliarySectionDefinition SectionDefinition;
};
/// The Import Directory Table.
///
/// There is a single array of these and one entry per imported DLL.
struct ImportDirectoryTableEntry {
uint32_t ImportLookupTableRVA;
uint32_t TimeDateStamp;
uint32_t ForwarderChain;
uint32_t NameRVA;
uint32_t ImportAddressTableRVA;
};
/// The PE32 Import Lookup Table.
///
/// There is an array of these for each imported DLL. It represents either
/// the ordinal to import from the target DLL, or a name to lookup and import
/// from the target DLL.
///
/// This also happens to be the same format used by the Import Address Table
/// when it is initially written out to the image.
struct ImportLookupTableEntry32 {
uint32_t data;
/// Is this entry specified by ordinal, or name?
bool isOrdinal() const { return data & 0x80000000; }
/// Get the ordinal value of this entry. isOrdinal must be true.
uint16_t getOrdinal() const {
assert(isOrdinal() && "ILT entry is not an ordinal!");
return data & 0xFFFF;
}
/// Set the ordinal value and set isOrdinal to true.
void setOrdinal(uint16_t o) {
data = o;
data |= 0x80000000;
}
/// Get the Hint/Name entry RVA. isOrdinal must be false.
uint32_t getHintNameRVA() const {
assert(!isOrdinal() && "ILT entry is not a Hint/Name RVA!");
return data;
}
/// Set the Hint/Name entry RVA and set isOrdinal to false.
void setHintNameRVA(uint32_t rva) { data = rva; }
};
/// The DOS compatible header at the front of all PEs.
struct DOSHeader {
uint16_t Magic;
uint16_t UsedBytesInTheLastPage;
uint16_t FileSizeInPages;
uint16_t NumberOfRelocationItems;
uint16_t HeaderSizeInParagraphs;
uint16_t MinimumExtraParagraphs;
uint16_t MaximumExtraParagraphs;
uint16_t InitialRelativeSS;
uint16_t InitialSP;
uint16_t Checksum;
uint16_t InitialIP;
uint16_t InitialRelativeCS;
uint16_t AddressOfRelocationTable;
uint16_t OverlayNumber;
uint16_t Reserved[4];
uint16_t OEMid;
uint16_t OEMinfo;
uint16_t Reserved2[10];
uint32_t AddressOfNewExeHeader;
};
struct PE32Header {
enum { PE32 = 0x10b, PE32_PLUS = 0x20b };
uint16_t Magic;
uint8_t MajorLinkerVersion;
uint8_t MinorLinkerVersion;
uint32_t SizeOfCode;
uint32_t SizeOfInitializedData;
uint32_t SizeOfUninitializedData;
uint32_t AddressOfEntryPoint; // RVA
uint32_t BaseOfCode; // RVA
uint32_t BaseOfData; // RVA
uint64_t ImageBase;
uint32_t SectionAlignment;
uint32_t FileAlignment;
uint16_t MajorOperatingSystemVersion;
uint16_t MinorOperatingSystemVersion;
uint16_t MajorImageVersion;
uint16_t MinorImageVersion;
uint16_t MajorSubsystemVersion;
uint16_t MinorSubsystemVersion;
uint32_t Win32VersionValue;
uint32_t SizeOfImage;
uint32_t SizeOfHeaders;
uint32_t CheckSum;
uint16_t Subsystem;
// FIXME: This should be DllCharacteristics to match the COFF spec.
uint16_t DLLCharacteristics;
uint64_t SizeOfStackReserve;
uint64_t SizeOfStackCommit;
uint64_t SizeOfHeapReserve;
uint64_t SizeOfHeapCommit;
uint32_t LoaderFlags;
// FIXME: This should be NumberOfRvaAndSizes to match the COFF spec.
uint32_t NumberOfRvaAndSize;
};
struct DataDirectory {
uint32_t RelativeVirtualAddress;
uint32_t Size;
};
enum DataDirectoryIndex : unsigned {
EXPORT_TABLE = 0,
IMPORT_TABLE,
RESOURCE_TABLE,
EXCEPTION_TABLE,
CERTIFICATE_TABLE,
BASE_RELOCATION_TABLE,
DEBUG_DIRECTORY,
ARCHITECTURE,
GLOBAL_PTR,
TLS_TABLE,
LOAD_CONFIG_TABLE,
BOUND_IMPORT,
IAT,
DELAY_IMPORT_DESCRIPTOR,
CLR_RUNTIME_HEADER,
NUM_DATA_DIRECTORIES
};
enum WindowsSubsystem : unsigned {
IMAGE_SUBSYSTEM_UNKNOWN = 0, ///< An unknown subsystem.
IMAGE_SUBSYSTEM_NATIVE = 1, ///< Device drivers and native Windows processes
IMAGE_SUBSYSTEM_WINDOWS_GUI = 2, ///< The Windows GUI subsystem.
IMAGE_SUBSYSTEM_WINDOWS_CUI = 3, ///< The Windows character subsystem.
IMAGE_SUBSYSTEM_OS2_CUI = 5, ///< The OS/2 character subsystem.
IMAGE_SUBSYSTEM_POSIX_CUI = 7, ///< The POSIX character subsystem.
IMAGE_SUBSYSTEM_NATIVE_WINDOWS = 8, ///< Native Windows 9x driver.
IMAGE_SUBSYSTEM_WINDOWS_CE_GUI = 9, ///< Windows CE.
IMAGE_SUBSYSTEM_EFI_APPLICATION = 10, ///< An EFI application.
IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER = 11, ///< An EFI driver with boot
/// services.
IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER = 12, ///< An EFI driver with run-time
/// services.
IMAGE_SUBSYSTEM_EFI_ROM = 13, ///< An EFI ROM image.
IMAGE_SUBSYSTEM_XBOX = 14, ///< XBOX.
IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION = 16 ///< A BCD application.
};
enum DLLCharacteristics : unsigned {
/// ASLR with 64 bit address space.
IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020,
/// DLL can be relocated at load time.
IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE = 0x0040,
/// Code integrity checks are enforced.
IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY = 0x0080,
///< Image is NX compatible.
IMAGE_DLL_CHARACTERISTICS_NX_COMPAT = 0x0100,
/// Isolation aware, but do not isolate the image.
IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION = 0x0200,
/// Does not use structured exception handling (SEH). No SEH handler may be
/// called in this image.
IMAGE_DLL_CHARACTERISTICS_NO_SEH = 0x0400,
/// Do not bind the image.
IMAGE_DLL_CHARACTERISTICS_NO_BIND = 0x0800,
///< Image should execute in an AppContainer.
IMAGE_DLL_CHARACTERISTICS_APPCONTAINER = 0x1000,
///< A WDM driver.
IMAGE_DLL_CHARACTERISTICS_WDM_DRIVER = 0x2000,
///< Image supports Control Flow Guard.
IMAGE_DLL_CHARACTERISTICS_GUARD_CF = 0x4000,
/// Terminal Server aware.
IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000
};
enum ExtendedDLLCharacteristics : unsigned {
/// Image is CET compatible
IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT = 0x0001
};
enum DebugType : unsigned {
IMAGE_DEBUG_TYPE_UNKNOWN = 0,
IMAGE_DEBUG_TYPE_COFF = 1,
IMAGE_DEBUG_TYPE_CODEVIEW = 2,
IMAGE_DEBUG_TYPE_FPO = 3,
IMAGE_DEBUG_TYPE_MISC = 4,
IMAGE_DEBUG_TYPE_EXCEPTION = 5,
IMAGE_DEBUG_TYPE_FIXUP = 6,
IMAGE_DEBUG_TYPE_OMAP_TO_SRC = 7,
IMAGE_DEBUG_TYPE_OMAP_FROM_SRC = 8,
IMAGE_DEBUG_TYPE_BORLAND = 9,
IMAGE_DEBUG_TYPE_RESERVED10 = 10,
IMAGE_DEBUG_TYPE_CLSID = 11,
IMAGE_DEBUG_TYPE_VC_FEATURE = 12,
IMAGE_DEBUG_TYPE_POGO = 13,
IMAGE_DEBUG_TYPE_ILTCG = 14,
IMAGE_DEBUG_TYPE_MPX = 15,
IMAGE_DEBUG_TYPE_REPRO = 16,
IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS = 20,
};
enum BaseRelocationType : unsigned {
IMAGE_REL_BASED_ABSOLUTE = 0,
IMAGE_REL_BASED_HIGH = 1,
IMAGE_REL_BASED_LOW = 2,
IMAGE_REL_BASED_HIGHLOW = 3,
IMAGE_REL_BASED_HIGHADJ = 4,
IMAGE_REL_BASED_MIPS_JMPADDR = 5,
IMAGE_REL_BASED_ARM_MOV32A = 5,
IMAGE_REL_BASED_ARM_MOV32T = 7,
IMAGE_REL_BASED_MIPS_JMPADDR16 = 9,
IMAGE_REL_BASED_DIR64 = 10
};
enum ImportType : unsigned {
IMPORT_CODE = 0,
IMPORT_DATA = 1,
IMPORT_CONST = 2
};
enum ImportNameType : unsigned {
/// Import is by ordinal. This indicates that the value in the Ordinal/Hint
/// field of the import header is the import's ordinal. If this constant is
/// not specified, then the Ordinal/Hint field should always be interpreted
/// as the import's hint.
IMPORT_ORDINAL = 0,
/// The import name is identical to the public symbol name
IMPORT_NAME = 1,
/// The import name is the public symbol name, but skipping the leading ?,
/// @, or optionally _.
IMPORT_NAME_NOPREFIX = 2,
/// The import name is the public symbol name, but skipping the leading ?,
/// @, or optionally _, and truncating at the first @.
IMPORT_NAME_UNDECORATE = 3,
/// The import name is specified as a separate string in the import library
/// object file.
IMPORT_NAME_EXPORTAS = 4
};
enum class GuardFlags : uint32_t {
/// Module performs control flow integrity checks using system-supplied
/// support.
CF_INSTRUMENTED = 0x100,
/// Module performs control flow and write integrity checks.
CFW_INSTRUMENTED = 0x200,
/// Module contains valid control flow target metadata.
CF_FUNCTION_TABLE_PRESENT = 0x400,
/// Module does not make use of the /GS security cookie.
SECURITY_COOKIE_UNUSED = 0x800,
/// Module supports read only delay load IAT.
PROTECT_DELAYLOAD_IAT = 0x1000,
/// Delayload import table in its own .didat section (with nothing else in it)
/// that can be freely reprotected.
DELAYLOAD_IAT_IN_ITS_OWN_SECTION = 0x2000,
/// Module contains suppressed export information. This also infers that the
/// address taken IAT table is also present in the load config.
CF_EXPORT_SUPPRESSION_INFO_PRESENT = 0x4000,
/// Module enables suppression of exports.
CF_ENABLE_EXPORT_SUPPRESSION = 0x8000,
/// Module contains longjmp target information.
CF_LONGJUMP_TABLE_PRESENT = 0x10000,
/// Module contains EH continuation target information.
EH_CONTINUATION_TABLE_PRESENT = 0x400000,
/// Mask for the subfield that contains the stride of Control Flow Guard
/// function table entries (that is, the additional count of bytes per table
/// entry).
CF_FUNCTION_TABLE_SIZE_MASK = 0xF0000000,
CF_FUNCTION_TABLE_SIZE_5BYTES = 0x10000000,
CF_FUNCTION_TABLE_SIZE_6BYTES = 0x20000000,
CF_FUNCTION_TABLE_SIZE_7BYTES = 0x30000000,
CF_FUNCTION_TABLE_SIZE_8BYTES = 0x40000000,
CF_FUNCTION_TABLE_SIZE_9BYTES = 0x50000000,
CF_FUNCTION_TABLE_SIZE_10BYTES = 0x60000000,
CF_FUNCTION_TABLE_SIZE_11BYTES = 0x70000000,
CF_FUNCTION_TABLE_SIZE_12BYTES = 0x80000000,
CF_FUNCTION_TABLE_SIZE_13BYTES = 0x90000000,
CF_FUNCTION_TABLE_SIZE_14BYTES = 0xA0000000,
CF_FUNCTION_TABLE_SIZE_15BYTES = 0xB0000000,
CF_FUNCTION_TABLE_SIZE_16BYTES = 0xC0000000,
CF_FUNCTION_TABLE_SIZE_17BYTES = 0xD0000000,
CF_FUNCTION_TABLE_SIZE_18BYTES = 0xE0000000,
CF_FUNCTION_TABLE_SIZE_19BYTES = 0xF0000000,
};
struct ImportHeader {
uint16_t Sig1; ///< Must be IMAGE_FILE_MACHINE_UNKNOWN (0).
uint16_t Sig2; ///< Must be 0xFFFF.
uint16_t Version;
uint16_t Machine;
uint32_t TimeDateStamp;
uint32_t SizeOfData;
uint16_t OrdinalHint;
uint16_t TypeInfo;
ImportType getType() const { return static_cast<ImportType>(TypeInfo & 0x3); }
ImportNameType getNameType() const {
return static_cast<ImportNameType>((TypeInfo & 0x1C) >> 2);
}
};
enum CodeViewIdentifiers {
DEBUG_SECTION_MAGIC = 0x4,
DEBUG_HASHES_SECTION_MAGIC = 0x133C9C5
};
// These flags show up in the @feat.00 symbol. They appear to be some kind of
// compiler features bitfield read by link.exe.
enum Feat00Flags : uint32_t {
// Object is compatible with /safeseh.
SafeSEH = 0x1,
// Object was compiled with /GS.
GuardStack = 0x100,
// Object was compiled with /sdl.
SDL = 0x200,
// Object was compiled with /guard:cf.
GuardCF = 0x800,
// Object was compiled with /guard:ehcont.
GuardEHCont = 0x4000,
// Object was compiled with /kernel.
Kernel = 0x40000000,
};
enum Arm64ECThunkType : uint8_t {
GuestExit = 0,
Entry = 1,
Exit = 4,
};
inline bool isReservedSectionNumber(int32_t SectionNumber) {
return SectionNumber <= 0;
}
/// Encode section name based on string table offset.
/// The size of Out must be at least COFF::NameSize.
bool encodeSectionName(char *Out, uint64_t Offset);
} // End namespace COFF.
} // End namespace llvm.
#endif

View File

@@ -0,0 +1,807 @@
//===- COFFImportFile.cpp - COFF short import file implementation ---------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines the writeImportLibrary function.
//
//===----------------------------------------------------------------------===//
#include "llvm/Object/COFFImportFile.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ArchiveWriter.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Path.h"
#include <cstdint>
#include <string>
#include <vector>
using namespace llvm::COFF;
using namespace llvm::object;
using namespace llvm;
namespace llvm {
namespace object {
StringRef COFFImportFile::getFileFormatName() const {
switch (getMachine()) {
case COFF::IMAGE_FILE_MACHINE_I386:
return "COFF-import-file-i386";
case COFF::IMAGE_FILE_MACHINE_AMD64:
return "COFF-import-file-x86-64";
case COFF::IMAGE_FILE_MACHINE_ARMNT:
return "COFF-import-file-ARM";
case COFF::IMAGE_FILE_MACHINE_ARM64:
return "COFF-import-file-ARM64";
case COFF::IMAGE_FILE_MACHINE_ARM64EC:
return "COFF-import-file-ARM64EC";
case COFF::IMAGE_FILE_MACHINE_ARM64X:
return "COFF-import-file-ARM64X";
default:
return "COFF-import-file-<unknown arch>";
}
}
static StringRef applyNameType(ImportNameType Type, StringRef name) {
auto ltrim1 = [](StringRef s, StringRef chars) {
return !s.empty() && chars.contains(s[0]) ? s.substr(1) : s;
};
switch (Type) {
case IMPORT_NAME_NOPREFIX:
name = ltrim1(name, "?@_");
break;
case IMPORT_NAME_UNDECORATE:
name = ltrim1(name, "?@_");
name = name.substr(0, name.find('@'));
break;
default:
break;
}
return name;
}
StringRef COFFImportFile::getExportName() const {
const coff_import_header *hdr = getCOFFImportHeader();
StringRef name = Data.getBuffer().substr(sizeof(*hdr)).split('\0').first;
switch (hdr->getNameType()) {
case IMPORT_ORDINAL:
name = "";
break;
case IMPORT_NAME_NOPREFIX:
case IMPORT_NAME_UNDECORATE:
name = applyNameType(static_cast<ImportNameType>(hdr->getNameType()), name);
break;
case IMPORT_NAME_EXPORTAS: {
// Skip DLL name
name = Data.getBuffer().substr(sizeof(*hdr) + name.size() + 1);
name = name.split('\0').second.split('\0').first;
break;
}
default:
break;
}
return name;
}
Error COFFImportFile::printSymbolName(raw_ostream &OS, DataRefImpl Symb) const {
switch (Symb.p) {
case ImpSymbol:
OS << "__imp_";
break;
case ECAuxSymbol:
OS << "__imp_aux_";
break;
}
const char *Name = Data.getBufferStart() + sizeof(coff_import_header);
if (Symb.p != ECThunkSymbol && COFF::isArm64EC(getMachine())) {
if (std::optional<std::string> DemangledName =
getArm64ECDemangledFunctionName(Name)) {
OS << StringRef(*DemangledName);
return Error::success();
}
}
OS << StringRef(Name);
return Error::success();
}
static uint16_t getImgRelRelocation(MachineTypes Machine) {
switch (Machine) {
default:
llvm_unreachable("unsupported machine");
case IMAGE_FILE_MACHINE_AMD64:
return IMAGE_REL_AMD64_ADDR32NB;
case IMAGE_FILE_MACHINE_ARMNT:
return IMAGE_REL_ARM_ADDR32NB;
case IMAGE_FILE_MACHINE_ARM64:
case IMAGE_FILE_MACHINE_ARM64EC:
case IMAGE_FILE_MACHINE_ARM64X:
return IMAGE_REL_ARM64_ADDR32NB;
case IMAGE_FILE_MACHINE_I386:
return IMAGE_REL_I386_DIR32NB;
case IMAGE_FILE_MACHINE_R4000:
return IMAGE_REL_MIPS_REFWORDNB;
}
}
template <class T> static void append(std::vector<uint8_t> &B, const T &Data) {
size_t S = B.size();
B.resize(S + sizeof(T));
memcpy(&B[S], &Data, sizeof(T));
}
static void writeStringTable(std::vector<uint8_t> &B,
ArrayRef<const std::string_view> Strings) {
// The COFF string table consists of a 4-byte value which is the size of the
// table, including the length field itself. This value is followed by the
// string content itself, which is an array of null-terminated C-style
// strings. The termination is important as they are referenced to by offset
// by the symbol entity in the file format.
size_t Pos = B.size();
size_t Offset = B.size();
// Skip over the length field, we will fill it in later as we will have
// computed the length while emitting the string content itself.
Pos += sizeof(uint32_t);
for (const auto &S : Strings) {
B.resize(Pos + S.length() + 1);
std::copy(S.begin(), S.end(), std::next(B.begin(), Pos));
B[Pos + S.length()] = 0;
Pos += S.length() + 1;
}
// Backfill the length of the table now that it has been computed.
support::ulittle32_t Length(B.size() - Offset);
support::endian::write32le(&B[Offset], Length);
}
static ImportNameType getNameType(StringRef Sym, StringRef ExtName,
MachineTypes Machine, bool MinGW) {
// A decorated stdcall function in MSVC is exported with the
// type IMPORT_NAME, and the exported function name includes the
// the leading underscore. In MinGW on the other hand, a decorated
// stdcall function still omits the underscore (IMPORT_NAME_NOPREFIX).
// See the comment in isDecorated in COFFModuleDefinition.cpp for more
// details.
if (ExtName.starts_with("_") && ExtName.contains('@') && !MinGW)
return IMPORT_NAME;
if (Sym != ExtName)
return IMPORT_NAME_UNDECORATE;
if (Machine == IMAGE_FILE_MACHINE_I386 && Sym.starts_with("_"))
return IMPORT_NAME_NOPREFIX;
return IMPORT_NAME;
}
static Expected<std::string> replace(StringRef S, StringRef From,
StringRef To) {
size_t Pos = S.find(From);
// From and To may be mangled, but substrings in S may not.
if (Pos == StringRef::npos && From.starts_with("_") && To.starts_with("_")) {
From = From.substr(1);
To = To.substr(1);
Pos = S.find(From);
}
if (Pos == StringRef::npos) {
return make_error<StringError>(
StringRef(Twine(S + ": replacing '" + From +
"' with '" + To + "' failed").str()), object_error::parse_failed);
}
return (Twine(S.substr(0, Pos)) + To + S.substr(Pos + From.size())).str();
}
namespace {
// This class constructs various small object files necessary to support linking
// symbols imported from a DLL. The contents are pretty strictly defined and
// nearly entirely static. The details of the structures files are defined in
// WINNT.h and the PE/COFF specification.
class ObjectFactory {
using u16 = support::ulittle16_t;
using u32 = support::ulittle32_t;
MachineTypes NativeMachine;
BumpPtrAllocator Alloc;
StringRef ImportName;
StringRef Library;
std::string ImportDescriptorSymbolName;
std::string NullThunkSymbolName;
public:
ObjectFactory(StringRef S, MachineTypes M)
: NativeMachine(M), ImportName(S), Library(llvm::sys::path::stem(S)),
ImportDescriptorSymbolName((ImportDescriptorPrefix + Library).str()),
NullThunkSymbolName(
(NullThunkDataPrefix + Library + NullThunkDataSuffix).str()) {}
// Creates an Import Descriptor. This is a small object file which contains a
// reference to the terminators and contains the library name (entry) for the
// import name table. It will force the linker to construct the necessary
// structure to import symbols from the DLL.
NewArchiveMember createImportDescriptor(std::vector<uint8_t> &Buffer);
// Creates a NULL import descriptor. This is a small object file whcih
// contains a NULL import descriptor. It is used to terminate the imports
// from a specific DLL.
NewArchiveMember createNullImportDescriptor(std::vector<uint8_t> &Buffer);
// Create a NULL Thunk Entry. This is a small object file which contains a
// NULL Import Address Table entry and a NULL Import Lookup Table Entry. It
// is used to terminate the IAT and ILT.
NewArchiveMember createNullThunk(std::vector<uint8_t> &Buffer);
// Create a short import file which is described in PE/COFF spec 7. Import
// Library Format.
NewArchiveMember createShortImport(StringRef Sym, uint16_t Ordinal,
ImportType Type, ImportNameType NameType,
StringRef ExportName,
MachineTypes Machine);
// Create a weak external file which is described in PE/COFF Aux Format 3.
NewArchiveMember createWeakExternal(StringRef Sym, StringRef Weak, bool Imp,
MachineTypes Machine);
bool is64Bit() const { return COFF::is64Bit(NativeMachine); }
};
} // namespace
NewArchiveMember
ObjectFactory::createImportDescriptor(std::vector<uint8_t> &Buffer) {
const uint32_t NumberOfSections = 2;
const uint32_t NumberOfSymbols = 7;
const uint32_t NumberOfRelocations = 3;
// COFF Header
coff_file_header Header{
u16(NativeMachine),
u16(NumberOfSections),
u32(0),
u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) +
// .idata$2
sizeof(coff_import_directory_table_entry) +
NumberOfRelocations * sizeof(coff_relocation) +
// .idata$4
(ImportName.size() + 1)),
u32(NumberOfSymbols),
u16(0),
u16(is64Bit() ? C_Invalid : IMAGE_FILE_32BIT_MACHINE),
};
append(Buffer, Header);
// Section Header Table
const coff_section SectionTable[NumberOfSections] = {
{{'.', 'i', 'd', 'a', 't', 'a', '$', '2'},
u32(0),
u32(0),
u32(sizeof(coff_import_directory_table_entry)),
u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)),
u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) +
sizeof(coff_import_directory_table_entry)),
u32(0),
u16(NumberOfRelocations),
u16(0),
u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA |
IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)},
{{'.', 'i', 'd', 'a', 't', 'a', '$', '6'},
u32(0),
u32(0),
u32(ImportName.size() + 1),
u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) +
sizeof(coff_import_directory_table_entry) +
NumberOfRelocations * sizeof(coff_relocation)),
u32(0),
u32(0),
u16(0),
u16(0),
u32(IMAGE_SCN_ALIGN_2BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA |
IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)},
};
append(Buffer, SectionTable);
// .idata$2
const coff_import_directory_table_entry ImportDescriptor{
u32(0), u32(0), u32(0), u32(0), u32(0),
};
append(Buffer, ImportDescriptor);
const coff_relocation RelocationTable[NumberOfRelocations] = {
{u32(offsetof(coff_import_directory_table_entry, NameRVA)), u32(2),
u16(getImgRelRelocation(NativeMachine))},
{u32(offsetof(coff_import_directory_table_entry, ImportLookupTableRVA)),
u32(3), u16(getImgRelRelocation(NativeMachine))},
{u32(offsetof(coff_import_directory_table_entry, ImportAddressTableRVA)),
u32(4), u16(getImgRelRelocation(NativeMachine))},
};
append(Buffer, RelocationTable);
// .idata$6
auto S = Buffer.size();
Buffer.resize(S + ImportName.size() + 1);
memcpy(&Buffer[S], ImportName.data(), ImportName.size());
Buffer[S + ImportName.size()] = '\0';
// Symbol Table
coff_symbol16 SymbolTable[NumberOfSymbols] = {
{{{0, 0, 0, 0, 0, 0, 0, 0}},
u32(0),
u16(1),
u16(0),
IMAGE_SYM_CLASS_EXTERNAL,
0},
{{{'.', 'i', 'd', 'a', 't', 'a', '$', '2'}},
u32(0),
u16(1),
u16(0),
IMAGE_SYM_CLASS_SECTION,
0},
{{{'.', 'i', 'd', 'a', 't', 'a', '$', '6'}},
u32(0),
u16(2),
u16(0),
IMAGE_SYM_CLASS_STATIC,
0},
{{{'.', 'i', 'd', 'a', 't', 'a', '$', '4'}},
u32(0),
u16(0),
u16(0),
IMAGE_SYM_CLASS_SECTION,
0},
{{{'.', 'i', 'd', 'a', 't', 'a', '$', '5'}},
u32(0),
u16(0),
u16(0),
IMAGE_SYM_CLASS_SECTION,
0},
{{{0, 0, 0, 0, 0, 0, 0, 0}},
u32(0),
u16(0),
u16(0),
IMAGE_SYM_CLASS_EXTERNAL,
0},
{{{0, 0, 0, 0, 0, 0, 0, 0}},
u32(0),
u16(0),
u16(0),
IMAGE_SYM_CLASS_EXTERNAL,
0},
};
// TODO: Name.Offset.Offset here and in the all similar places below
// suggests a names refactoring. Maybe StringTableOffset.Value?
SymbolTable[0].Name.Offset.Offset =
sizeof(uint32_t);
SymbolTable[5].Name.Offset.Offset =
sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1;
SymbolTable[6].Name.Offset.Offset =
sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1 +
NullImportDescriptorSymbolName.length() + 1;
append(Buffer, SymbolTable);
// String Table
writeStringTable(Buffer,
{ImportDescriptorSymbolName, NullImportDescriptorSymbolName,
NullThunkSymbolName});
StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()};
return {MemoryBufferRef(F, ImportName)};
}
NewArchiveMember
ObjectFactory::createNullImportDescriptor(std::vector<uint8_t> &Buffer) {
const uint32_t NumberOfSections = 1;
const uint32_t NumberOfSymbols = 1;
// COFF Header
coff_file_header Header{
u16(NativeMachine),
u16(NumberOfSections),
u32(0),
u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) +
// .idata$3
sizeof(coff_import_directory_table_entry)),
u32(NumberOfSymbols),
u16(0),
u16(is64Bit() ? C_Invalid : IMAGE_FILE_32BIT_MACHINE),
};
append(Buffer, Header);
// Section Header Table
const coff_section SectionTable[NumberOfSections] = {
{{'.', 'i', 'd', 'a', 't', 'a', '$', '3'},
u32(0),
u32(0),
u32(sizeof(coff_import_directory_table_entry)),
u32(sizeof(coff_file_header) +
(NumberOfSections * sizeof(coff_section))),
u32(0),
u32(0),
u16(0),
u16(0),
u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA |
IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)},
};
append(Buffer, SectionTable);
// .idata$3
const coff_import_directory_table_entry ImportDescriptor{
u32(0), u32(0), u32(0), u32(0), u32(0),
};
append(Buffer, ImportDescriptor);
// Symbol Table
coff_symbol16 SymbolTable[NumberOfSymbols] = {
{{{0, 0, 0, 0, 0, 0, 0, 0}},
u32(0),
u16(1),
u16(0),
IMAGE_SYM_CLASS_EXTERNAL,
0},
};
SymbolTable[0].Name.Offset.Offset = sizeof(uint32_t);
append(Buffer, SymbolTable);
// String Table
writeStringTable(Buffer, {NullImportDescriptorSymbolName});
StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()};
return {MemoryBufferRef(F, ImportName)};
}
NewArchiveMember ObjectFactory::createNullThunk(std::vector<uint8_t> &Buffer) {
const uint32_t NumberOfSections = 2;
const uint32_t NumberOfSymbols = 1;
uint32_t VASize = is64Bit() ? 8 : 4;
// COFF Header
coff_file_header Header{
u16(NativeMachine),
u16(NumberOfSections),
u32(0),
u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) +
// .idata$5
VASize +
// .idata$4
VASize),
u32(NumberOfSymbols),
u16(0),
u16(is64Bit() ? C_Invalid : IMAGE_FILE_32BIT_MACHINE),
};
append(Buffer, Header);
// Section Header Table
const coff_section SectionTable[NumberOfSections] = {
{{'.', 'i', 'd', 'a', 't', 'a', '$', '5'},
u32(0),
u32(0),
u32(VASize),
u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)),
u32(0),
u32(0),
u16(0),
u16(0),
u32((is64Bit() ? IMAGE_SCN_ALIGN_8BYTES : IMAGE_SCN_ALIGN_4BYTES) |
IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ |
IMAGE_SCN_MEM_WRITE)},
{{'.', 'i', 'd', 'a', 't', 'a', '$', '4'},
u32(0),
u32(0),
u32(VASize),
u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) +
VASize),
u32(0),
u32(0),
u16(0),
u16(0),
u32((is64Bit() ? IMAGE_SCN_ALIGN_8BYTES : IMAGE_SCN_ALIGN_4BYTES) |
IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ |
IMAGE_SCN_MEM_WRITE)},
};
append(Buffer, SectionTable);
// .idata$5, ILT
append(Buffer, u32(0));
if (is64Bit())
append(Buffer, u32(0));
// .idata$4, IAT
append(Buffer, u32(0));
if (is64Bit())
append(Buffer, u32(0));
// Symbol Table
coff_symbol16 SymbolTable[NumberOfSymbols] = {
{{{0, 0, 0, 0, 0, 0, 0, 0}},
u32(0),
u16(1),
u16(0),
IMAGE_SYM_CLASS_EXTERNAL,
0},
};
SymbolTable[0].Name.Offset.Offset = sizeof(uint32_t);
append(Buffer, SymbolTable);
// String Table
writeStringTable(Buffer, {NullThunkSymbolName});
StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()};
return {MemoryBufferRef{F, ImportName}};
}
NewArchiveMember
ObjectFactory::createShortImport(StringRef Sym, uint16_t Ordinal,
ImportType ImportType, ImportNameType NameType,
StringRef ExportName, MachineTypes Machine) {
size_t ImpSize = ImportName.size() + Sym.size() + 2; // +2 for NULs
if (!ExportName.empty())
ImpSize += ExportName.size() + 1;
size_t Size = sizeof(coff_import_header) + ImpSize;
char *Buf = Alloc.Allocate<char>(Size);
memset(Buf, 0, Size);
char *P = Buf;
// Write short import library.
auto *Imp = reinterpret_cast<coff_import_header *>(P);
P += sizeof(*Imp);
Imp->Sig2 = 0xFFFF;
Imp->Machine = Machine;
Imp->SizeOfData = ImpSize;
if (Ordinal > 0)
Imp->OrdinalHint = Ordinal;
Imp->TypeInfo = (NameType << 2) | ImportType;
// Write symbol name and DLL name.
memcpy(P, Sym.data(), Sym.size());
P += Sym.size() + 1;
memcpy(P, ImportName.data(), ImportName.size());
if (!ExportName.empty()) {
P += ImportName.size() + 1;
memcpy(P, ExportName.data(), ExportName.size());
}
return {MemoryBufferRef(StringRef(Buf, Size), ImportName)};
}
NewArchiveMember ObjectFactory::createWeakExternal(StringRef Sym,
StringRef Weak, bool Imp,
MachineTypes Machine) {
std::vector<uint8_t> Buffer;
const uint32_t NumberOfSections = 1;
const uint32_t NumberOfSymbols = 5;
// COFF Header
coff_file_header Header{
u16(Machine),
u16(NumberOfSections),
u32(0),
u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section))),
u32(NumberOfSymbols),
u16(0),
u16(0),
};
append(Buffer, Header);
// Section Header Table
const coff_section SectionTable[NumberOfSections] = {
{{'.', 'd', 'r', 'e', 'c', 't', 'v', 'e'},
u32(0),
u32(0),
u32(0),
u32(0),
u32(0),
u32(0),
u16(0),
u16(0),
u32(IMAGE_SCN_LNK_INFO | IMAGE_SCN_LNK_REMOVE)}};
append(Buffer, SectionTable);
// Symbol Table
coff_symbol16 SymbolTable[NumberOfSymbols] = {
{{{'@', 'c', 'o', 'm', 'p', '.', 'i', 'd'}},
u32(0),
u16(0xFFFF),
u16(0),
IMAGE_SYM_CLASS_STATIC,
0},
{{{'@', 'f', 'e', 'a', 't', '.', '0', '0'}},
u32(0),
u16(0xFFFF),
u16(0),
IMAGE_SYM_CLASS_STATIC,
0},
{{{0, 0, 0, 0, 0, 0, 0, 0}},
u32(0),
u16(0),
u16(0),
IMAGE_SYM_CLASS_EXTERNAL,
0},
{{{0, 0, 0, 0, 0, 0, 0, 0}},
u32(0),
u16(0),
u16(0),
IMAGE_SYM_CLASS_WEAK_EXTERNAL,
1},
{{{2, 0, 0, 0, IMAGE_WEAK_EXTERN_SEARCH_ALIAS, 0, 0, 0}},
u32(0),
u16(0),
u16(0),
IMAGE_SYM_CLASS_NULL,
0},
};
SymbolTable[2].Name.Offset.Offset = sizeof(uint32_t);
//__imp_ String Table
StringRef Prefix = Imp ? "__imp_" : "";
SymbolTable[3].Name.Offset.Offset =
sizeof(uint32_t) + Sym.size() + Prefix.size() + 1;
append(Buffer, SymbolTable);
writeStringTable(Buffer, {(Prefix + Sym).str(),
(Prefix + Weak).str()});
// Copied here so we can still use writeStringTable
char *Buf = Alloc.Allocate<char>(Buffer.size());
memcpy(Buf, Buffer.data(), Buffer.size());
return {MemoryBufferRef(StringRef(Buf, Buffer.size()), ImportName)};
}
Error writeImportLibrary(StringRef ImportName, StringRef Path,
ArrayRef<COFFShortExport> Exports,
MachineTypes Machine, bool MinGW,
ArrayRef<COFFShortExport> NativeExports) {
MachineTypes NativeMachine = Machine;
if (isArm64EC(Machine)) {
NativeMachine = IMAGE_FILE_MACHINE_ARM64;
Machine = IMAGE_FILE_MACHINE_ARM64EC;
}
std::vector<NewArchiveMember> Members;
ObjectFactory OF(llvm::sys::path::filename(ImportName), NativeMachine);
std::vector<uint8_t> ImportDescriptor;
Members.push_back(OF.createImportDescriptor(ImportDescriptor));
std::vector<uint8_t> NullImportDescriptor;
Members.push_back(OF.createNullImportDescriptor(NullImportDescriptor));
std::vector<uint8_t> NullThunk;
Members.push_back(OF.createNullThunk(NullThunk));
auto addExports = [&](ArrayRef<COFFShortExport> Exp,
MachineTypes M) -> Error {
StringMap<std::string> RegularImports;
struct Deferred {
std::string Name;
ImportType ImpType;
const COFFShortExport *Export;
};
SmallVector<Deferred, 0> Renames;
for (const COFFShortExport &E : Exp) {
if (E.Private)
continue;
ImportType ImportType = IMPORT_CODE;
if (E.Data)
ImportType = IMPORT_DATA;
if (E.Constant)
ImportType = IMPORT_CONST;
StringRef SymbolName = E.SymbolName.empty() ? E.Name : E.SymbolName;
std::string Name;
if (E.ExtName.empty()) {
Name = std::string(SymbolName);
} else {
Expected<std::string> ReplacedName =
object::replace(SymbolName, E.Name, E.ExtName);
if (!ReplacedName)
return ReplacedName.takeError();
Name.swap(*ReplacedName);
}
ImportNameType NameType;
std::string ExportName;
if (E.Noname) {
NameType = IMPORT_ORDINAL;
} else if (!E.ExportAs.empty()) {
NameType = IMPORT_NAME_EXPORTAS;
ExportName = E.ExportAs;
} else if (!E.ImportName.empty()) {
// If we need to import from a specific ImportName, we may need to use
// a weak alias (which needs another import to point at). But if we can
// express ImportName based on the symbol name and a specific NameType,
// prefer that over an alias.
if (Machine == IMAGE_FILE_MACHINE_I386 &&
applyNameType(IMPORT_NAME_UNDECORATE, Name) == E.ImportName)
NameType = IMPORT_NAME_UNDECORATE;
else if (Machine == IMAGE_FILE_MACHINE_I386 &&
applyNameType(IMPORT_NAME_NOPREFIX, Name) == E.ImportName)
NameType = IMPORT_NAME_NOPREFIX;
else if (isArm64EC(M)) {
NameType = IMPORT_NAME_EXPORTAS;
ExportName = E.ImportName;
} else if (Name == E.ImportName)
NameType = IMPORT_NAME;
else {
Deferred D;
D.Name = Name;
D.ImpType = ImportType;
D.Export = &E;
Renames.push_back(D);
continue;
}
} else {
NameType = getNameType(SymbolName, E.Name, M, MinGW);
}
// On ARM64EC, use EXPORTAS to import demangled name for mangled symbols.
if (ImportType == IMPORT_CODE && isArm64EC(M)) {
if (std::optional<std::string> MangledName =
getArm64ECMangledFunctionName(Name)) {
if (!E.Noname && ExportName.empty()) {
NameType = IMPORT_NAME_EXPORTAS;
ExportName.swap(Name);
}
Name = std::move(*MangledName);
} else if (!E.Noname && ExportName.empty()) {
std::optional<std::string> DemangledName =
getArm64ECDemangledFunctionName(Name);
if (!DemangledName)
return make_error<StringError>(
StringRef(Twine("Invalid ARM64EC function name '" + Name + "'")
.str()),
object_error::parse_failed);
NameType = IMPORT_NAME_EXPORTAS;
ExportName = std::move(*DemangledName);
}
}
RegularImports[applyNameType(NameType, Name)] = Name;
Members.push_back(OF.createShortImport(Name, E.Ordinal, ImportType,
NameType, ExportName, M));
}
for (const auto &D : Renames) {
auto It = RegularImports.find(D.Export->ImportName);
if (It != RegularImports.end()) {
// We have a regular import entry for a symbol with the name we
// want to reference; produce an alias pointing at that.
StringRef Symbol = It->second;
if (D.ImpType == IMPORT_CODE)
Members.push_back(OF.createWeakExternal(Symbol, D.Name, false, M));
Members.push_back(OF.createWeakExternal(Symbol, D.Name, true, M));
} else {
Members.push_back(OF.createShortImport(D.Name, D.Export->Ordinal,
D.ImpType, IMPORT_NAME_EXPORTAS,
D.Export->ImportName, M));
}
}
return Error::success();
};
if (Error e = addExports(Exports, Machine))
return e;
if (Error e = addExports(NativeExports, NativeMachine))
return e;
return writeArchive(Path, Members, SymtabWritingMode::NormalSymtab,
object::Archive::K_COFF,
/*Deterministic*/ true, /*Thin*/ false,
/*OldArchiveBuf*/ nullptr, isArm64EC(Machine));
}
} // namespace object
} // namespace llvm

View File

@@ -0,0 +1,146 @@
//===- COFFImportFile.h - COFF short import file implementation -*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// COFF short import file is a special kind of file which contains
// only symbol names for DLL-exported symbols. This class implements
// exporting of Symbols to create libraries and a SymbolicFile
// interface for the file type.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_OBJECT_COFFIMPORTFILE_H
#define LLVM_OBJECT_COFFIMPORTFILE_H
#include "llvm/ADT/ArrayRef.h"
#include "llvm/IR/Mangler.h"
#include "llvm/Object/COFF.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Object/SymbolicFile.h"
#include "llvm/Support/MemoryBufferRef.h"
#include "llvm/Support/raw_ostream.h"
namespace llvm {
namespace object {
constexpr std::string_view ImportDescriptorPrefix = "__IMPORT_DESCRIPTOR_";
constexpr std::string_view NullImportDescriptorSymbolName =
"__NULL_IMPORT_DESCRIPTOR";
constexpr std::string_view NullThunkDataPrefix = "\x7f";
constexpr std::string_view NullThunkDataSuffix = "_NULL_THUNK_DATA";
class COFFImportFile : public SymbolicFile {
private:
enum SymbolIndex { ImpSymbol, ThunkSymbol, ECAuxSymbol, ECThunkSymbol };
public:
COFFImportFile(MemoryBufferRef Source)
: SymbolicFile(ID_COFFImportFile, Source) {}
static bool classof(Binary const *V) { return V->isCOFFImportFile(); }
void moveSymbolNext(DataRefImpl &Symb) const override { ++Symb.p; }
Error printSymbolName(raw_ostream &OS, DataRefImpl Symb) const override;
Expected<uint32_t> getSymbolFlags(DataRefImpl Symb) const override {
return SymbolRef::SF_Global;
}
basic_symbol_iterator symbol_begin() const override {
return BasicSymbolRef(DataRefImpl(), this);
}
basic_symbol_iterator symbol_end() const override {
DataRefImpl Symb;
if (isData())
Symb.p = ImpSymbol + 1;
else if (COFF::isArm64EC(getMachine()))
Symb.p = ECThunkSymbol + 1;
else
Symb.p = ThunkSymbol + 1;
return BasicSymbolRef(Symb, this);
}
bool is64Bit() const override { return false; }
const coff_import_header *getCOFFImportHeader() const {
return reinterpret_cast<const object::coff_import_header *>(
Data.getBufferStart());
}
uint16_t getMachine() const { return getCOFFImportHeader()->Machine; }
StringRef getFileFormatName() const;
StringRef getExportName() const;
private:
bool isData() const {
return getCOFFImportHeader()->getType() == COFF::IMPORT_DATA;
}
};
struct COFFShortExport {
/// The name of the export as specified in the .def file or on the command
/// line, i.e. "foo" in "/EXPORT:foo", and "bar" in "/EXPORT:foo=bar". This
/// may lack mangling, such as underscore prefixing and stdcall suffixing.
std::string Name;
/// The external, exported name. Only non-empty when export renaming is in
/// effect, i.e. "foo" in "/EXPORT:foo=bar".
std::string ExtName;
/// The real, mangled symbol name from the object file. Given
/// "/export:foo=bar", this could be "_bar@8" if bar is stdcall.
std::string SymbolName;
/// Creates an import library entry that imports from a DLL export with a
/// different name. This is the name of the DLL export that should be
/// referenced when linking against this import library entry. In a .def
/// file, this is "baz" in "EXPORTS\nfoo = bar == baz".
std::string ImportName;
/// Specifies EXPORTAS name. In a .def file, this is "bar" in
/// "EXPORTS\nfoo EXPORTAS bar".
std::string ExportAs;
uint16_t Ordinal = 0;
bool Noname = false;
bool Data = false;
bool Private = false;
bool Constant = false;
friend bool operator==(const COFFShortExport &L, const COFFShortExport &R) {
return L.Name == R.Name && L.ExtName == R.ExtName &&
L.Ordinal == R.Ordinal && L.Noname == R.Noname &&
L.Data == R.Data && L.Private == R.Private;
}
friend bool operator!=(const COFFShortExport &L, const COFFShortExport &R) {
return !(L == R);
}
};
/// Writes a COFF import library containing entries described by the Exports
/// array.
///
/// For hybrid targets such as ARM64EC, additional native entry points can be
/// exposed using the NativeExports parameter. When NativeExports is used, the
/// output import library will expose these native ARM64 imports alongside the
/// entries described in the Exports array. Such a library can be used for
/// linking both ARM64EC and pure ARM64 objects, and the linker will pick only
/// the exports relevant to the target platform. For non-hybrid targets,
/// the NativeExports parameter should not be used.
Error writeImportLibrary(StringRef ImportName, StringRef Path,
ArrayRef<COFFShortExport> Exports,
COFF::MachineTypes Machine, bool MinGW,
ArrayRef<COFFShortExport> NativeExports = {});
} // namespace object
} // namespace llvm
#endif

View File

@@ -0,0 +1,329 @@
//===-- Mangler.cpp - Self-contained c/asm llvm name mangler --------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Unified name mangler for assembly backends.
//
//===----------------------------------------------------------------------===//
#include "llvm/IR/Mangler.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Demangle/Demangle.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/Triple.h"
using namespace llvm;
namespace {
enum ManglerPrefixTy {
Default, ///< Emit default string before each symbol.
Private, ///< Emit "private" prefix before each symbol.
LinkerPrivate ///< Emit "linker private" prefix before each symbol.
};
}
static void getNameWithPrefixImpl(raw_ostream &OS, const Twine &GVName,
ManglerPrefixTy PrefixTy,
const DataLayout &DL, char Prefix) {
SmallString<256> TmpData;
StringRef Name = GVName.toStringRef(TmpData);
assert(!Name.empty() && "getNameWithPrefix requires non-empty name");
// No need to do anything special if the global has the special "do not
// mangle" flag in the name.
if (Name[0] == '\1') {
OS << Name.substr(1);
return;
}
if (DL.doNotMangleLeadingQuestionMark() && Name[0] == '?')
Prefix = '\0';
if (PrefixTy == Private)
OS << DL.getPrivateGlobalPrefix();
else if (PrefixTy == LinkerPrivate)
OS << DL.getLinkerPrivateGlobalPrefix();
if (Prefix != '\0')
OS << Prefix;
// If this is a simple string that doesn't need escaping, just append it.
OS << Name;
}
static void getNameWithPrefixImpl(raw_ostream &OS, const Twine &GVName,
const DataLayout &DL,
ManglerPrefixTy PrefixTy) {
char Prefix = DL.getGlobalPrefix();
return getNameWithPrefixImpl(OS, GVName, PrefixTy, DL, Prefix);
}
void Mangler::getNameWithPrefix(raw_ostream &OS, const Twine &GVName,
const DataLayout &DL) {
return getNameWithPrefixImpl(OS, GVName, DL, Default);
}
void Mangler::getNameWithPrefix(SmallVectorImpl<char> &OutName,
const Twine &GVName, const DataLayout &DL) {
raw_svector_ostream OS(OutName);
char Prefix = DL.getGlobalPrefix();
return getNameWithPrefixImpl(OS, GVName, Default, DL, Prefix);
}
static bool hasByteCountSuffix(CallingConv::ID CC) {
switch (CC) {
case CallingConv::X86_FastCall:
case CallingConv::X86_StdCall:
case CallingConv::X86_VectorCall:
return true;
default:
return false;
}
}
/// Microsoft fastcall and stdcall functions require a suffix on their name
/// indicating the number of words of arguments they take.
static void addByteCountSuffix(raw_ostream &OS, const Function *F,
const DataLayout &DL) {
// Calculate arguments size total.
unsigned ArgWords = 0;
const unsigned PtrSize = DL.getPointerSize();
for (const Argument &A : F->args()) {
// For the purposes of the byte count suffix, structs returned by pointer
// do not count as function arguments.
if (A.hasStructRetAttr())
continue;
// 'Dereference' type in case of byval or inalloca parameter attribute.
uint64_t AllocSize = A.hasPassPointeeByValueCopyAttr() ?
A.getPassPointeeByValueCopySize(DL) :
DL.getTypeAllocSize(A.getType());
// Size should be aligned to pointer size.
ArgWords += alignTo(AllocSize, PtrSize);
}
OS << '@' << ArgWords;
}
void Mangler::getNameWithPrefix(raw_ostream &OS, const GlobalValue *GV,
bool CannotUsePrivateLabel) const {
ManglerPrefixTy PrefixTy = Default;
assert(GV != nullptr && "Invalid Global Value");
if (GV->hasPrivateLinkage()) {
if (CannotUsePrivateLabel)
PrefixTy = LinkerPrivate;
else
PrefixTy = Private;
}
const DataLayout &DL = GV->getDataLayout();
if (!GV->hasName()) {
// Get the ID for the global, assigning a new one if we haven't got one
// already.
unsigned &ID = AnonGlobalIDs[GV];
if (ID == 0)
ID = AnonGlobalIDs.size();
// Must mangle the global into a unique ID.
getNameWithPrefixImpl(OS, "__unnamed_" + Twine(ID), DL, PrefixTy);
return;
}
StringRef Name = GV->getName();
char Prefix = DL.getGlobalPrefix();
// Mangle functions with Microsoft calling conventions specially. Only do
// this mangling for x86_64 vectorcall and 32-bit x86.
const Function *MSFunc = dyn_cast_or_null<Function>(GV->getAliaseeObject());
// Don't add byte count suffixes when '\01' or '?' are in the first
// character.
if (Name.starts_with("\01") ||
(DL.doNotMangleLeadingQuestionMark() && Name.starts_with("?")))
MSFunc = nullptr;
CallingConv::ID CC =
MSFunc ? MSFunc->getCallingConv() : (unsigned)CallingConv::C;
if (!DL.hasMicrosoftFastStdCallMangling() &&
CC != CallingConv::X86_VectorCall)
MSFunc = nullptr;
if (MSFunc) {
if (CC == CallingConv::X86_FastCall)
Prefix = '@'; // fastcall functions have an @ prefix instead of _.
else if (CC == CallingConv::X86_VectorCall)
Prefix = '\0'; // vectorcall functions have no prefix.
}
getNameWithPrefixImpl(OS, Name, PrefixTy, DL, Prefix);
if (!MSFunc)
return;
// If we are supposed to add a microsoft-style suffix for stdcall, fastcall,
// or vectorcall, add it. These functions have a suffix of @N where N is the
// cumulative byte size of all of the parameters to the function in decimal.
if (CC == CallingConv::X86_VectorCall)
OS << '@'; // vectorcall functions use a double @ suffix.
FunctionType *FT = MSFunc->getFunctionType();
if (hasByteCountSuffix(CC) &&
// "Pure" variadic functions do not receive @0 suffix.
(!FT->isVarArg() || FT->getNumParams() == 0 ||
(FT->getNumParams() == 1 && MSFunc->hasStructRetAttr())))
addByteCountSuffix(OS, MSFunc, DL);
}
void Mangler::getNameWithPrefix(SmallVectorImpl<char> &OutName,
const GlobalValue *GV,
bool CannotUsePrivateLabel) const {
raw_svector_ostream OS(OutName);
getNameWithPrefix(OS, GV, CannotUsePrivateLabel);
}
// Check if the name needs quotes to be safe for the linker to interpret.
static bool canBeUnquotedInDirective(char C) {
return isAlnum(C) || C == '_' || C == '@' || C == '#';
}
static bool canBeUnquotedInDirective(StringRef Name) {
if (Name.empty())
return false;
// If any of the characters in the string is an unacceptable character, force
// quotes.
for (char C : Name) {
if (!canBeUnquotedInDirective(C))
return false;
}
return true;
}
void llvm::emitLinkerFlagsForGlobalCOFF(raw_ostream &OS, const GlobalValue *GV,
const Triple &TT, Mangler &Mangler) {
if (GV->hasDLLExportStorageClass() && !GV->isDeclaration()) {
if (TT.isWindowsMSVCEnvironment())
OS << " /EXPORT:";
else
OS << " -export:";
bool NeedQuotes = GV->hasName() && !canBeUnquotedInDirective(GV->getName());
if (NeedQuotes)
OS << "\"";
if (TT.isWindowsGNUEnvironment() || TT.isWindowsCygwinEnvironment()) {
std::string Flag;
raw_string_ostream FlagOS(Flag);
Mangler.getNameWithPrefix(FlagOS, GV, false);
FlagOS.flush();
if (Flag[0] == GV->getDataLayout().getGlobalPrefix())
OS << Flag.substr(1);
else
OS << Flag;
} else {
Mangler.getNameWithPrefix(OS, GV, false);
}
if (TT.isWindowsArm64EC()) {
// Use EXPORTAS for mangled ARM64EC symbols.
// FIXME: During LTO, we're invoked prior to the EC lowering pass,
// so symbols are not yet mangled. Emitting the unmangled name
// typically functions correctly; the linker can resolve the export
// with the demangled alias.
if (std::optional<std::string> demangledName =
getArm64ECDemangledFunctionName(GV->getName()))
OS << ",EXPORTAS," << *demangledName;
}
if (NeedQuotes)
OS << "\"";
if (!GV->getValueType()->isFunctionTy()) {
if (TT.isWindowsMSVCEnvironment())
OS << ",DATA";
else
OS << ",data";
}
}
if (GV->hasHiddenVisibility() && !GV->isDeclaration() && TT.isOSCygMing()) {
OS << " -exclude-symbols:";
bool NeedQuotes = GV->hasName() && !canBeUnquotedInDirective(GV->getName());
if (NeedQuotes)
OS << "\"";
std::string Flag;
raw_string_ostream FlagOS(Flag);
Mangler.getNameWithPrefix(FlagOS, GV, false);
FlagOS.flush();
if (Flag[0] == GV->getDataLayout().getGlobalPrefix())
OS << Flag.substr(1);
else
OS << Flag;
if (NeedQuotes)
OS << "\"";
}
}
void llvm::emitLinkerFlagsForUsedCOFF(raw_ostream &OS, const GlobalValue *GV,
const Triple &T, Mangler &M) {
if (!T.isWindowsMSVCEnvironment())
return;
OS << " /INCLUDE:";
bool NeedQuotes = GV->hasName() && !canBeUnquotedInDirective(GV->getName());
if (NeedQuotes)
OS << "\"";
M.getNameWithPrefix(OS, GV, false);
if (NeedQuotes)
OS << "\"";
}
std::optional<std::string> llvm::getArm64ECMangledFunctionName(StringRef Name) {
if (Name[0] != '?') {
// For non-C++ symbols, prefix the name with "#" unless it's already
// mangled.
if (Name[0] == '#')
return std::nullopt;
return std::optional<std::string>(("#" + Name).str());
}
// If the name contains $$h, then it is already mangled.
if (Name.contains("$$h"))
return std::nullopt;
// Ask the demangler where we should insert "$$h".
auto InsertIdx = getArm64ECInsertionPointInMangledName(Name);
if (!InsertIdx)
return std::nullopt;
return std::optional<std::string>(
(Name.substr(0, *InsertIdx) + "$$h" + Name.substr(*InsertIdx)).str());
}
std::optional<std::string>
llvm::getArm64ECDemangledFunctionName(StringRef Name) {
// For non-C++ names, drop the "#" prefix.
if (Name[0] == '#')
return std::optional<std::string>(Name.substr(1));
if (Name[0] != '?')
return std::nullopt;
// Drop the ARM64EC "$$h" tag.
std::pair<StringRef, StringRef> Pair = Name.split("$$h");
if (Pair.second.empty())
return std::nullopt;
return std::optional<std::string>((Pair.first + Pair.second).str());
}

View File

@@ -0,0 +1,273 @@
//===- llvm/unittest/IR/ManglerTest.cpp - Mangler unit tests --------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/IR/Mangler.h"
#include "llvm/IR/CallingConv.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/Module.h"
#include "gtest/gtest.h"
using namespace llvm;
static std::string mangleStr(StringRef IRName, Mangler &Mang,
const DataLayout &DL) {
std::string Mangled;
raw_string_ostream SS(Mangled);
Mang.getNameWithPrefix(SS, IRName, DL);
return Mangled;
}
static std::string mangleFunc(StringRef IRName,
GlobalValue::LinkageTypes Linkage,
llvm::CallingConv::ID CC, Module &Mod,
Mangler &Mang) {
Type *VoidTy = Type::getVoidTy(Mod.getContext());
Type *I32Ty = Type::getInt32Ty(Mod.getContext());
FunctionType *FTy =
FunctionType::get(VoidTy, {I32Ty, I32Ty, I32Ty}, /*isVarArg=*/false);
Function *F = Function::Create(FTy, Linkage, IRName, &Mod);
F->setCallingConv(CC);
std::string Mangled;
raw_string_ostream SS(Mangled);
Mang.getNameWithPrefix(SS, F, false);
F->eraseFromParent();
return Mangled;
}
namespace {
TEST(ManglerTest, MachO) {
LLVMContext Ctx;
DataLayout DL("m:o"); // macho
Module Mod("test", Ctx);
Mod.setDataLayout(DL);
Mangler Mang;
EXPECT_EQ(mangleStr("foo", Mang, DL), "_foo");
EXPECT_EQ(mangleStr("\01foo", Mang, DL), "foo");
EXPECT_EQ(mangleStr("?foo", Mang, DL), "_?foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::C, Mod, Mang),
"_foo");
EXPECT_EQ(mangleFunc("?foo", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::C, Mod, Mang),
"_?foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::PrivateLinkage,
llvm::CallingConv::C, Mod, Mang),
"L_foo");
}
TEST(ManglerTest, WindowsX86) {
LLVMContext Ctx;
DataLayout DL("m:x-p:32:32"); // 32-bit windows
Module Mod("test", Ctx);
Mod.setDataLayout(DL);
Mangler Mang;
EXPECT_EQ(mangleStr("foo", Mang, DL), "_foo");
EXPECT_EQ(mangleStr("\01foo", Mang, DL), "foo");
EXPECT_EQ(mangleStr("?foo", Mang, DL), "?foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::C, Mod, Mang),
"_foo");
EXPECT_EQ(mangleFunc("?foo", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::C, Mod, Mang),
"?foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::PrivateLinkage,
llvm::CallingConv::C, Mod, Mang),
"L_foo");
// Test calling conv mangling.
EXPECT_EQ(mangleFunc("stdcall", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::X86_StdCall, Mod, Mang),
"_stdcall@12");
EXPECT_EQ(mangleFunc("fastcall", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::X86_FastCall, Mod, Mang),
"@fastcall@12");
EXPECT_EQ(mangleFunc("vectorcall", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::X86_VectorCall, Mod, Mang),
"vectorcall@@12");
// Adding a '?' prefix blocks calling convention mangling.
EXPECT_EQ(mangleFunc("?fastcall", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::X86_FastCall, Mod, Mang),
"?fastcall");
}
TEST(ManglerTest, WindowsX64) {
LLVMContext Ctx;
DataLayout DL("m:w-p:64:64"); // windows
Module Mod("test", Ctx);
Mod.setDataLayout(DL);
Mangler Mang;
EXPECT_EQ(mangleStr("foo", Mang, DL), "foo");
EXPECT_EQ(mangleStr("\01foo", Mang, DL), "foo");
EXPECT_EQ(mangleStr("?foo", Mang, DL), "?foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::C, Mod, Mang),
"foo");
EXPECT_EQ(mangleFunc("?foo", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::C, Mod, Mang),
"?foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::PrivateLinkage,
llvm::CallingConv::C, Mod, Mang),
".Lfoo");
// Test calling conv mangling.
EXPECT_EQ(mangleFunc("stdcall", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::X86_StdCall, Mod, Mang),
"stdcall");
EXPECT_EQ(mangleFunc("fastcall", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::X86_FastCall, Mod, Mang),
"fastcall");
EXPECT_EQ(mangleFunc("vectorcall", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::X86_VectorCall, Mod, Mang),
"vectorcall@@24");
// Adding a '?' prefix blocks calling convention mangling.
EXPECT_EQ(mangleFunc("?vectorcall", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::X86_VectorCall, Mod, Mang),
"?vectorcall");
}
TEST(ManglerTest, UEFIX64) {
LLVMContext Ctx;
DataLayout DL("e-m:w-p270:32:32-p271:32:32-p272:64:64-"
"i64:64-i128:128-f80:128-n8:16:32:64-S128"); // uefi X86_64
Module Mod("test", Ctx);
Mod.setDataLayout(DL);
Mangler Mang;
EXPECT_EQ(mangleStr("foo", Mang, DL), "foo");
EXPECT_EQ(mangleStr("\01foo", Mang, DL), "foo");
EXPECT_EQ(mangleStr("?foo", Mang, DL), "?foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::C, Mod, Mang),
"foo");
EXPECT_EQ(mangleFunc("?foo", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::C, Mod, Mang),
"?foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::PrivateLinkage,
llvm::CallingConv::C, Mod, Mang),
".Lfoo");
}
TEST(ManglerTest, XCOFF) {
LLVMContext Ctx;
DataLayout DL("m:a"); // XCOFF/AIX
Module Mod("test", Ctx);
Mod.setDataLayout(DL);
Mangler Mang;
EXPECT_EQ(mangleStr("foo", Mang, DL), "foo");
EXPECT_EQ(mangleStr("\01foo", Mang, DL), "foo");
EXPECT_EQ(mangleStr("?foo", Mang, DL), "?foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::C, Mod, Mang),
"foo");
EXPECT_EQ(mangleFunc("?foo", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::C, Mod, Mang),
"?foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::PrivateLinkage,
llvm::CallingConv::C, Mod, Mang),
"L..foo");
}
TEST(ManglerTest, GOFF) {
LLVMContext Ctx;
DataLayout DL("m:l"); // GOFF
Module Mod("test", Ctx);
Mod.setDataLayout(DL);
Mangler Mang;
EXPECT_EQ(mangleStr("foo", Mang, DL), "foo");
EXPECT_EQ(mangleStr("\01foo", Mang, DL), "foo");
EXPECT_EQ(mangleStr("?foo", Mang, DL), "?foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::C, Mod, Mang),
"foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::PrivateLinkage,
llvm::CallingConv::C, Mod, Mang),
"L#foo");
}
TEST(ManglerTest, Arm64EC) {
constexpr std::string_view Arm64ECNames[] = {
// Basic C name.
"#Foo",
// Basic C++ name.
"?foo@@$$hYAHXZ",
// Regression test: https://github.com/llvm/llvm-project/issues/115231
"?GetValue@?$Wrapper@UA@@@@$$hQEBAHXZ",
// Symbols from:
// ```
// namespace A::B::C::D {
// struct Base {
// virtual int f() { return 0; }
// };
// }
// struct Derived : public A::B::C::D::Base {
// virtual int f() override { return 1; }
// };
// A::B::C::D::Base* MakeObj() { return new Derived(); }
// ```
// void * __cdecl operator new(unsigned __int64)
"??2@$$hYAPEAX_K@Z",
// public: virtual int __cdecl A::B::C::D::Base::f(void)
"?f@Base@D@C@B@A@@$$hUEAAHXZ",
// public: __cdecl A::B::C::D::Base::Base(void)
"??0Base@D@C@B@A@@$$hQEAA@XZ",
// public: virtual int __cdecl Derived::f(void)
"?f@Derived@@$$hUEAAHXZ",
// public: __cdecl Derived::Derived(void)
"??0Derived@@$$hQEAA@XZ",
// struct A::B::C::D::Base * __cdecl MakeObj(void)
"?MakeObj@@$$hYAPEAUBase@D@C@B@A@@XZ",
// Symbols from:
// ```
// template <typename T> struct WW { struct Z{}; };
// template <typename X> struct Wrapper {
// int GetValue(typename WW<X>::Z) const;
// };
// struct A { };
// template <typename X> int Wrapper<X>::GetValue(typename WW<X>::Z) const
// { return 3; }
// template class Wrapper<A>;
// ```
// public: int __cdecl Wrapper<struct A>::GetValue(struct WW<struct
// A>::Z)const
"?GetValue@?$Wrapper@UA@@@@$$hQEBAHUZ@?$WW@UA@@@@@Z",
};
for (const auto &Arm64ECName : Arm64ECNames) {
// Check that this is a mangled name.
EXPECT_TRUE(isArm64ECMangledFunctionName(Arm64ECName))
<< "Test case: " << Arm64ECName;
// Refuse to mangle it again.
EXPECT_FALSE(getArm64ECMangledFunctionName(Arm64ECName).has_value())
<< "Test case: " << Arm64ECName;
// Demangle.
auto Arm64Name = getArm64ECDemangledFunctionName(Arm64ECName);
EXPECT_TRUE(Arm64Name.has_value()) << "Test case: " << Arm64ECName;
// Check that it is not mangled.
EXPECT_FALSE(isArm64ECMangledFunctionName(Arm64Name.value()))
<< "Test case: " << Arm64ECName;
// Refuse to demangle it again.
EXPECT_FALSE(getArm64ECDemangledFunctionName(Arm64Name.value()).has_value())
<< "Test case: " << Arm64ECName;
// Round-trip.
auto RoundTripArm64ECName =
getArm64ECMangledFunctionName(Arm64Name.value());
EXPECT_EQ(RoundTripArm64ECName, Arm64ECName);
}
}
} // end anonymous namespace

View File

@@ -0,0 +1,792 @@
//===-- llvm/Support/MathExtras.h - Useful math functions -------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains some functions that are useful for math stuff.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_SUPPORT_MATHEXTRAS_H
#define LLVM_SUPPORT_MATHEXTRAS_H
#include "llvm/ADT/bit.h"
#include "llvm/Support/Compiler.h"
#include <cassert>
#include <climits>
#include <cstdint>
#include <cstring>
#include <limits>
#include <type_traits>
namespace llvm {
/// Some template parameter helpers to optimize for bitwidth, for functions that
/// take multiple arguments.
// We can't verify signedness, since callers rely on implicit coercions to
// signed/unsigned.
template <typename T, typename U>
using enableif_int =
std::enable_if_t<std::is_integral_v<T> && std::is_integral_v<U>>;
// Use std::common_type_t to widen only up to the widest argument.
template <typename T, typename U, typename = enableif_int<T, U>>
using common_uint =
std::common_type_t<std::make_unsigned_t<T>, std::make_unsigned_t<U>>;
template <typename T, typename U, typename = enableif_int<T, U>>
using common_sint =
std::common_type_t<std::make_signed_t<T>, std::make_signed_t<U>>;
/// Mathematical constants.
namespace numbers {
// TODO: Track C++20 std::numbers.
// clang-format off
constexpr double e = 0x1.5bf0a8b145769P+1, // (2.7182818284590452354) https://oeis.org/A001113
egamma = 0x1.2788cfc6fb619P-1, // (.57721566490153286061) https://oeis.org/A001620
ln2 = 0x1.62e42fefa39efP-1, // (.69314718055994530942) https://oeis.org/A002162
ln10 = 0x1.26bb1bbb55516P+1, // (2.3025850929940456840) https://oeis.org/A002392
log2e = 0x1.71547652b82feP+0, // (1.4426950408889634074)
log10e = 0x1.bcb7b1526e50eP-2, // (.43429448190325182765)
pi = 0x1.921fb54442d18P+1, // (3.1415926535897932385) https://oeis.org/A000796
inv_pi = 0x1.45f306dc9c883P-2, // (.31830988618379067154) https://oeis.org/A049541
sqrtpi = 0x1.c5bf891b4ef6bP+0, // (1.7724538509055160273) https://oeis.org/A002161
inv_sqrtpi = 0x1.20dd750429b6dP-1, // (.56418958354775628695) https://oeis.org/A087197
sqrt2 = 0x1.6a09e667f3bcdP+0, // (1.4142135623730950488) https://oeis.org/A00219
inv_sqrt2 = 0x1.6a09e667f3bcdP-1, // (.70710678118654752440)
sqrt3 = 0x1.bb67ae8584caaP+0, // (1.7320508075688772935) https://oeis.org/A002194
inv_sqrt3 = 0x1.279a74590331cP-1, // (.57735026918962576451)
phi = 0x1.9e3779b97f4a8P+0; // (1.6180339887498948482) https://oeis.org/A001622
constexpr float ef = 0x1.5bf0a8P+1F, // (2.71828183) https://oeis.org/A001113
egammaf = 0x1.2788d0P-1F, // (.577215665) https://oeis.org/A001620
ln2f = 0x1.62e430P-1F, // (.693147181) https://oeis.org/A002162
ln10f = 0x1.26bb1cP+1F, // (2.30258509) https://oeis.org/A002392
log2ef = 0x1.715476P+0F, // (1.44269504)
log10ef = 0x1.bcb7b2P-2F, // (.434294482)
pif = 0x1.921fb6P+1F, // (3.14159265) https://oeis.org/A000796
inv_pif = 0x1.45f306P-2F, // (.318309886) https://oeis.org/A049541
sqrtpif = 0x1.c5bf8aP+0F, // (1.77245385) https://oeis.org/A002161
inv_sqrtpif = 0x1.20dd76P-1F, // (.564189584) https://oeis.org/A087197
sqrt2f = 0x1.6a09e6P+0F, // (1.41421356) https://oeis.org/A002193
inv_sqrt2f = 0x1.6a09e6P-1F, // (.707106781)
sqrt3f = 0x1.bb67aeP+0F, // (1.73205081) https://oeis.org/A002194
inv_sqrt3f = 0x1.279a74P-1F, // (.577350269)
phif = 0x1.9e377aP+0F; // (1.61803399) https://oeis.org/A001622
// clang-format on
} // namespace numbers
/// Create a bitmask with the N right-most bits set to 1, and all other
/// bits set to 0. Only unsigned types are allowed.
template <typename T> T maskTrailingOnes(unsigned N) {
static_assert(std::is_unsigned_v<T>, "Invalid type!");
const unsigned Bits = CHAR_BIT * sizeof(T);
assert(N <= Bits && "Invalid bit index");
if (N == 0)
return 0;
return T(-1) >> (Bits - N);
}
/// Create a bitmask with the N left-most bits set to 1, and all other
/// bits set to 0. Only unsigned types are allowed.
template <typename T> T maskLeadingOnes(unsigned N) {
return ~maskTrailingOnes<T>(CHAR_BIT * sizeof(T) - N);
}
/// Create a bitmask with the N right-most bits set to 0, and all other
/// bits set to 1. Only unsigned types are allowed.
template <typename T> T maskTrailingZeros(unsigned N) {
return maskLeadingOnes<T>(CHAR_BIT * sizeof(T) - N);
}
/// Create a bitmask with the N left-most bits set to 0, and all other
/// bits set to 1. Only unsigned types are allowed.
template <typename T> T maskLeadingZeros(unsigned N) {
return maskTrailingOnes<T>(CHAR_BIT * sizeof(T) - N);
}
/// Macro compressed bit reversal table for 256 bits.
///
/// http://graphics.stanford.edu/~seander/bithacks.html#BitReverseTable
static const unsigned char BitReverseTable256[256] = {
#define R2(n) n, n + 2 * 64, n + 1 * 64, n + 3 * 64
#define R4(n) R2(n), R2(n + 2 * 16), R2(n + 1 * 16), R2(n + 3 * 16)
#define R6(n) R4(n), R4(n + 2 * 4), R4(n + 1 * 4), R4(n + 3 * 4)
R6(0), R6(2), R6(1), R6(3)
#undef R2
#undef R4
#undef R6
};
/// Reverse the bits in \p Val.
template <typename T> T reverseBits(T Val) {
#if __has_builtin(__builtin_bitreverse8)
if constexpr (std::is_same_v<T, uint8_t>)
return __builtin_bitreverse8(Val);
#endif
#if __has_builtin(__builtin_bitreverse16)
if constexpr (std::is_same_v<T, uint16_t>)
return __builtin_bitreverse16(Val);
#endif
#if __has_builtin(__builtin_bitreverse32)
if constexpr (std::is_same_v<T, uint32_t>)
return __builtin_bitreverse32(Val);
#endif
#if __has_builtin(__builtin_bitreverse64)
if constexpr (std::is_same_v<T, uint64_t>)
return __builtin_bitreverse64(Val);
#endif
unsigned char in[sizeof(Val)];
unsigned char out[sizeof(Val)];
std::memcpy(in, &Val, sizeof(Val));
for (unsigned i = 0; i < sizeof(Val); ++i)
out[(sizeof(Val) - i) - 1] = BitReverseTable256[in[i]];
std::memcpy(&Val, out, sizeof(Val));
return Val;
}
// NOTE: The following support functions use the _32/_64 extensions instead of
// type overloading so that signed and unsigned integers can be used without
// ambiguity.
/// Return the high 32 bits of a 64 bit value.
constexpr uint32_t Hi_32(uint64_t Value) {
return static_cast<uint32_t>(Value >> 32);
}
/// Return the low 32 bits of a 64 bit value.
constexpr uint32_t Lo_32(uint64_t Value) {
return static_cast<uint32_t>(Value);
}
/// Make a 64-bit integer from a high / low pair of 32-bit integers.
constexpr uint64_t Make_64(uint32_t High, uint32_t Low) {
return ((uint64_t)High << 32) | (uint64_t)Low;
}
/// Checks if an integer fits into the given bit width.
template <unsigned N> constexpr bool isInt(int64_t x) {
if constexpr (N == 0)
return 0 == x;
if constexpr (N == 8)
return static_cast<int8_t>(x) == x;
if constexpr (N == 16)
return static_cast<int16_t>(x) == x;
if constexpr (N == 32)
return static_cast<int32_t>(x) == x;
if constexpr (N < 64)
return -(INT64_C(1) << (N - 1)) <= x && x < (INT64_C(1) << (N - 1));
(void)x; // MSVC v19.25 warns that x is unused.
return true;
}
/// Checks if a signed integer is an N bit number shifted left by S.
template <unsigned N, unsigned S>
constexpr bool isShiftedInt(int64_t x) {
static_assert(S < 64, "isShiftedInt<N, S> with S >= 64 is too much.");
static_assert(N + S <= 64, "isShiftedInt<N, S> with N + S > 64 is too wide.");
return isInt<N + S>(x) && (x % (UINT64_C(1) << S) == 0);
}
/// Checks if an unsigned integer fits into the given bit width.
template <unsigned N> constexpr bool isUInt(uint64_t x) {
if constexpr (N == 0)
return 0 == x;
if constexpr (N == 8)
return static_cast<uint8_t>(x) == x;
if constexpr (N == 16)
return static_cast<uint16_t>(x) == x;
if constexpr (N == 32)
return static_cast<uint32_t>(x) == x;
if constexpr (N < 64)
return x < (UINT64_C(1) << (N));
(void)x; // MSVC v19.25 warns that x is unused.
return true;
}
/// Checks if a unsigned integer is an N bit number shifted left by S.
template <unsigned N, unsigned S>
constexpr bool isShiftedUInt(uint64_t x) {
static_assert(S < 64, "isShiftedUInt<N, S> with S >= 64 is too much.");
static_assert(N + S <= 64,
"isShiftedUInt<N, S> with N + S > 64 is too wide.");
// S must be strictly less than 64. So 1 << S is not undefined behavior.
return isUInt<N + S>(x) && (x % (UINT64_C(1) << S) == 0);
}
/// Gets the maximum value for a N-bit unsigned integer.
inline uint64_t maxUIntN(uint64_t N) {
assert(N <= 64 && "integer width out of range");
// uint64_t(1) << 64 is undefined behavior, so we can't do
// (uint64_t(1) << N) - 1
// without checking first that N != 64. But this works and doesn't have a
// branch for N != 0.
// Unfortunately, shifting a uint64_t right by 64 bit is undefined
// behavior, so the condition on N == 0 is necessary. Fortunately, most
// optimizers do not emit branches for this check.
if (N == 0)
return 0;
return UINT64_MAX >> (64 - N);
}
/// Gets the minimum value for a N-bit signed integer.
inline int64_t minIntN(int64_t N) {
assert(N <= 64 && "integer width out of range");
if (N == 0)
return 0;
return UINT64_C(1) + ~(UINT64_C(1) << (N - 1));
}
/// Gets the maximum value for a N-bit signed integer.
inline int64_t maxIntN(int64_t N) {
assert(N <= 64 && "integer width out of range");
// This relies on two's complement wraparound when N == 64, so we convert to
// int64_t only at the very end to avoid UB.
if (N == 0)
return 0;
return (UINT64_C(1) << (N - 1)) - 1;
}
/// Checks if an unsigned integer fits into the given (dynamic) bit width.
inline bool isUIntN(unsigned N, uint64_t x) {
return N >= 64 || x <= maxUIntN(N);
}
/// Checks if an signed integer fits into the given (dynamic) bit width.
inline bool isIntN(unsigned N, int64_t x) {
return N >= 64 || (minIntN(N) <= x && x <= maxIntN(N));
}
/// Return true if the argument is a non-empty sequence of ones starting at the
/// least significant bit with the remainder zero (32 bit version).
/// Ex. isMask_32(0x0000FFFFU) == true.
constexpr bool isMask_32(uint32_t Value) {
return Value && ((Value + 1) & Value) == 0;
}
/// Return true if the argument is a non-empty sequence of ones starting at the
/// least significant bit with the remainder zero (64 bit version).
constexpr bool isMask_64(uint64_t Value) {
return Value && ((Value + 1) & Value) == 0;
}
/// Return true if the argument contains a non-empty sequence of ones with the
/// remainder zero (32 bit version.) Ex. isShiftedMask_32(0x0000FF00U) == true.
constexpr bool isShiftedMask_32(uint32_t Value) {
return Value && isMask_32((Value - 1) | Value);
}
/// Return true if the argument contains a non-empty sequence of ones with the
/// remainder zero (64 bit version.)
constexpr bool isShiftedMask_64(uint64_t Value) {
return Value && isMask_64((Value - 1) | Value);
}
/// Return true if the argument is a power of two > 0.
/// Ex. isPowerOf2_32(0x00100000U) == true (32 bit edition.)
constexpr bool isPowerOf2_32(uint32_t Value) {
return llvm::has_single_bit(Value);
}
/// Return true if the argument is a power of two > 0 (64 bit edition.)
constexpr bool isPowerOf2_64(uint64_t Value) {
return llvm::has_single_bit(Value);
}
/// Return true if the argument contains a non-empty sequence of ones with the
/// remainder zero (32 bit version.) Ex. isShiftedMask_32(0x0000FF00U) == true.
/// If true, \p MaskIdx will specify the index of the lowest set bit and \p
/// MaskLen is updated to specify the length of the mask, else neither are
/// updated.
inline bool isShiftedMask_32(uint32_t Value, unsigned &MaskIdx,
unsigned &MaskLen) {
if (!isShiftedMask_32(Value))
return false;
MaskIdx = llvm::countr_zero(Value);
MaskLen = llvm::popcount(Value);
return true;
}
/// Return true if the argument contains a non-empty sequence of ones with the
/// remainder zero (64 bit version.) If true, \p MaskIdx will specify the index
/// of the lowest set bit and \p MaskLen is updated to specify the length of the
/// mask, else neither are updated.
inline bool isShiftedMask_64(uint64_t Value, unsigned &MaskIdx,
unsigned &MaskLen) {
if (!isShiftedMask_64(Value))
return false;
MaskIdx = llvm::countr_zero(Value);
MaskLen = llvm::popcount(Value);
return true;
}
/// Compile time Log2.
/// Valid only for positive powers of two.
template <size_t kValue> constexpr size_t CTLog2() {
static_assert(kValue > 0 && llvm::isPowerOf2_64(kValue),
"Value is not a valid power of 2");
return 1 + CTLog2<kValue / 2>();
}
template <> constexpr size_t CTLog2<1>() { return 0; }
/// Return the floor log base 2 of the specified value, -1 if the value is zero.
/// (32 bit edition.)
/// Ex. Log2_32(32) == 5, Log2_32(1) == 0, Log2_32(0) == -1, Log2_32(6) == 2
inline unsigned Log2_32(uint32_t Value) {
return 31 - llvm::countl_zero(Value);
}
/// Return the floor log base 2 of the specified value, -1 if the value is zero.
/// (64 bit edition.)
inline unsigned Log2_64(uint64_t Value) {
return 63 - llvm::countl_zero(Value);
}
/// Return the ceil log base 2 of the specified value, 32 if the value is zero.
/// (32 bit edition).
/// Ex. Log2_32_Ceil(32) == 5, Log2_32_Ceil(1) == 0, Log2_32_Ceil(6) == 3
inline unsigned Log2_32_Ceil(uint32_t Value) {
return 32 - llvm::countl_zero(Value - 1);
}
/// Return the ceil log base 2 of the specified value, 64 if the value is zero.
/// (64 bit edition.)
inline unsigned Log2_64_Ceil(uint64_t Value) {
return 64 - llvm::countl_zero(Value - 1);
}
/// A and B are either alignments or offsets. Return the minimum alignment that
/// may be assumed after adding the two together.
template <typename U, typename V, typename T = common_uint<U, V>>
constexpr T MinAlign(U A, V B) {
// The largest power of 2 that divides both A and B.
//
// Replace "-Value" by "1+~Value" in the following commented code to avoid
// MSVC warning C4146
// return (A | B) & -(A | B);
return (A | B) & (1 + ~(A | B));
}
/// Fallback when arguments aren't integral.
constexpr uint64_t MinAlign(uint64_t A, uint64_t B) {
return (A | B) & (1 + ~(A | B));
}
/// Returns the next power of two (in 64-bits) that is strictly greater than A.
/// Returns zero on overflow.
constexpr uint64_t NextPowerOf2(uint64_t A) {
A |= (A >> 1);
A |= (A >> 2);
A |= (A >> 4);
A |= (A >> 8);
A |= (A >> 16);
A |= (A >> 32);
return A + 1;
}
/// Returns the power of two which is greater than or equal to the given value.
/// Essentially, it is a ceil operation across the domain of powers of two.
inline uint64_t PowerOf2Ceil(uint64_t A) {
if (!A || A > UINT64_MAX / 2)
return 0;
return UINT64_C(1) << Log2_64_Ceil(A);
}
/// Returns the integer ceil(Numerator / Denominator). Unsigned version.
/// Guaranteed to never overflow.
template <typename U, typename V, typename T = common_uint<U, V>>
constexpr T divideCeil(U Numerator, V Denominator) {
assert(Denominator && "Division by zero");
T Bias = (Numerator != 0);
return (Numerator - Bias) / Denominator + Bias;
}
/// Fallback when arguments aren't integral.
constexpr uint64_t divideCeil(uint64_t Numerator, uint64_t Denominator) {
assert(Denominator && "Division by zero");
uint64_t Bias = (Numerator != 0);
return (Numerator - Bias) / Denominator + Bias;
}
// Check whether divideCeilSigned or divideFloorSigned would overflow. This
// happens only when Numerator = INT_MIN and Denominator = -1.
template <typename U, typename V>
constexpr bool divideSignedWouldOverflow(U Numerator, V Denominator) {
return Numerator == std::numeric_limits<U>::min() && Denominator == -1;
}
/// Returns the integer ceil(Numerator / Denominator). Signed version.
/// Overflow is explicitly forbidden with an assert.
template <typename U, typename V, typename T = common_sint<U, V>>
constexpr T divideCeilSigned(U Numerator, V Denominator) {
assert(Denominator && "Division by zero");
assert(!divideSignedWouldOverflow(Numerator, Denominator) &&
"Divide would overflow");
if (!Numerator)
return 0;
// C's integer division rounds towards 0.
T Bias = Denominator >= 0 ? 1 : -1;
bool SameSign = (Numerator >= 0) == (Denominator >= 0);
return SameSign ? (Numerator - Bias) / Denominator + 1
: Numerator / Denominator;
}
/// Returns the integer floor(Numerator / Denominator). Signed version.
/// Overflow is explicitly forbidden with an assert.
template <typename U, typename V, typename T = common_sint<U, V>>
constexpr T divideFloorSigned(U Numerator, V Denominator) {
assert(Denominator && "Division by zero");
assert(!divideSignedWouldOverflow(Numerator, Denominator) &&
"Divide would overflow");
if (!Numerator)
return 0;
// C's integer division rounds towards 0.
T Bias = Denominator >= 0 ? -1 : 1;
bool SameSign = (Numerator >= 0) == (Denominator >= 0);
return SameSign ? Numerator / Denominator
: (Numerator - Bias) / Denominator - 1;
}
/// Returns the remainder of the Euclidean division of LHS by RHS. Result is
/// always non-negative.
template <typename U, typename V, typename T = common_sint<U, V>>
constexpr T mod(U Numerator, V Denominator) {
assert(Denominator >= 1 && "Mod by non-positive number");
T Mod = Numerator % Denominator;
return Mod < 0 ? Mod + Denominator : Mod;
}
/// Returns (Numerator / Denominator) rounded by round-half-up. Guaranteed to
/// never overflow.
template <typename U, typename V, typename T = common_uint<U, V>>
constexpr T divideNearest(U Numerator, V Denominator) {
assert(Denominator && "Division by zero");
T Mod = Numerator % Denominator;
return (Numerator / Denominator) +
(Mod > (static_cast<T>(Denominator) - 1) / 2);
}
/// Returns the next integer (mod 2**nbits) that is greater than or equal to
/// \p Value and is a multiple of \p Align. \p Align must be non-zero.
///
/// Examples:
/// \code
/// alignTo(5, 8) = 8
/// alignTo(17, 8) = 24
/// alignTo(~0LL, 8) = 0
/// alignTo(321, 255) = 510
/// \endcode
///
/// Will overflow only if result is not representable in T.
template <typename U, typename V, typename T = common_uint<U, V>>
constexpr T alignTo(U Value, V Align) {
assert(Align != 0u && "Align can't be 0.");
T CeilDiv = divideCeil(Value, Align);
return CeilDiv * Align;
}
/// Fallback when arguments aren't integral.
constexpr uint64_t alignTo(uint64_t Value, uint64_t Align) {
assert(Align != 0u && "Align can't be 0.");
uint64_t CeilDiv = divideCeil(Value, Align);
return CeilDiv * Align;
}
/// Will overflow only if result is not representable in T.
template <typename U, typename V, typename T = common_uint<U, V>>
constexpr T alignToPowerOf2(U Value, V Align) {
assert(Align != 0 && (Align & (Align - 1)) == 0 &&
"Align must be a power of 2");
T NegAlign = static_cast<T>(0) - Align;
return (Value + (Align - 1)) & NegAlign;
}
/// Fallback when arguments aren't integral.
constexpr uint64_t alignToPowerOf2(uint64_t Value, uint64_t Align) {
assert(Align != 0 && (Align & (Align - 1)) == 0 &&
"Align must be a power of 2");
uint64_t NegAlign = 0 - Align;
return (Value + (Align - 1)) & NegAlign;
}
/// If non-zero \p Skew is specified, the return value will be a minimal integer
/// that is greater than or equal to \p Size and equal to \p A * N + \p Skew for
/// some integer N. If \p Skew is larger than \p A, its value is adjusted to '\p
/// Skew mod \p A'. \p Align must be non-zero.
///
/// Examples:
/// \code
/// alignTo(5, 8, 7) = 7
/// alignTo(17, 8, 1) = 17
/// alignTo(~0LL, 8, 3) = 3
/// alignTo(321, 255, 42) = 552
/// \endcode
///
/// May overflow.
template <typename U, typename V, typename W,
typename T = common_uint<common_uint<U, V>, W>>
constexpr T alignTo(U Value, V Align, W Skew) {
assert(Align != 0u && "Align can't be 0.");
Skew %= Align;
return alignTo(Value - Skew, Align) + Skew;
}
/// Returns the next integer (mod 2**nbits) that is greater than or equal to
/// \p Value and is a multiple of \c Align. \c Align must be non-zero.
///
/// Will overflow only if result is not representable in T.
template <auto Align, typename V, typename T = common_uint<decltype(Align), V>>
constexpr T alignTo(V Value) {
static_assert(Align != 0u, "Align must be non-zero");
T CeilDiv = divideCeil(Value, Align);
return CeilDiv * Align;
}
/// Returns the largest unsigned integer less than or equal to \p Value and is
/// \p Skew mod \p Align. \p Align must be non-zero. Guaranteed to never
/// overflow.
template <typename U, typename V, typename W = uint8_t,
typename T = common_uint<common_uint<U, V>, W>>
constexpr T alignDown(U Value, V Align, W Skew = 0) {
assert(Align != 0u && "Align can't be 0.");
Skew %= Align;
return (Value - Skew) / Align * Align + Skew;
}
/// Sign-extend the number in the bottom B bits of X to a 32-bit integer.
/// Requires B <= 32.
template <unsigned B> constexpr int32_t SignExtend32(uint32_t X) {
static_assert(B <= 32, "Bit width out of range.");
if constexpr (B == 0)
return 0;
return int32_t(X << (32 - B)) >> (32 - B);
}
/// Sign-extend the number in the bottom B bits of X to a 32-bit integer.
/// Requires B <= 32.
inline int32_t SignExtend32(uint32_t X, unsigned B) {
assert(B <= 32 && "Bit width out of range.");
if (B == 0)
return 0;
return int32_t(X << (32 - B)) >> (32 - B);
}
/// Sign-extend the number in the bottom B bits of X to a 64-bit integer.
/// Requires B <= 64.
template <unsigned B> constexpr int64_t SignExtend64(uint64_t x) {
static_assert(B <= 64, "Bit width out of range.");
if constexpr (B == 0)
return 0;
return int64_t(x << (64 - B)) >> (64 - B);
}
/// Sign-extend the number in the bottom B bits of X to a 64-bit integer.
/// Requires B <= 64.
inline int64_t SignExtend64(uint64_t X, unsigned B) {
assert(B <= 64 && "Bit width out of range.");
if (B == 0)
return 0;
return int64_t(X << (64 - B)) >> (64 - B);
}
/// Subtract two unsigned integers, X and Y, of type T and return the absolute
/// value of the result.
template <typename U, typename V, typename T = common_uint<U, V>>
constexpr T AbsoluteDifference(U X, V Y) {
return X > Y ? (X - Y) : (Y - X);
}
/// Add two unsigned integers, X and Y, of type T. Clamp the result to the
/// maximum representable value of T on overflow. ResultOverflowed indicates if
/// the result is larger than the maximum representable value of type T.
template <typename T>
std::enable_if_t<std::is_unsigned_v<T>, T>
SaturatingAdd(T X, T Y, bool *ResultOverflowed = nullptr) {
bool Dummy;
bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
// Hacker's Delight, p. 29
T Z = X + Y;
Overflowed = (Z < X || Z < Y);
if (Overflowed)
return std::numeric_limits<T>::max();
else
return Z;
}
/// Add multiple unsigned integers of type T. Clamp the result to the
/// maximum representable value of T on overflow.
template <class T, class... Ts>
std::enable_if_t<std::is_unsigned_v<T>, T> SaturatingAdd(T X, T Y, T Z,
Ts... Args) {
bool Overflowed = false;
T XY = SaturatingAdd(X, Y, &Overflowed);
if (Overflowed)
return SaturatingAdd(std::numeric_limits<T>::max(), T(1), Args...);
return SaturatingAdd(XY, Z, Args...);
}
/// Multiply two unsigned integers, X and Y, of type T. Clamp the result to the
/// maximum representable value of T on overflow. ResultOverflowed indicates if
/// the result is larger than the maximum representable value of type T.
template <typename T>
std::enable_if_t<std::is_unsigned_v<T>, T>
SaturatingMultiply(T X, T Y, bool *ResultOverflowed = nullptr) {
bool Dummy;
bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
// Hacker's Delight, p. 30 has a different algorithm, but we don't use that
// because it fails for uint16_t (where multiplication can have undefined
// behavior due to promotion to int), and requires a division in addition
// to the multiplication.
Overflowed = false;
// Log2(Z) would be either Log2Z or Log2Z + 1.
// Special case: if X or Y is 0, Log2_64 gives -1, and Log2Z
// will necessarily be less than Log2Max as desired.
int Log2Z = Log2_64(X) + Log2_64(Y);
const T Max = std::numeric_limits<T>::max();
int Log2Max = Log2_64(Max);
if (Log2Z < Log2Max) {
return X * Y;
}
if (Log2Z > Log2Max) {
Overflowed = true;
return Max;
}
// We're going to use the top bit, and maybe overflow one
// bit past it. Multiply all but the bottom bit then add
// that on at the end.
T Z = (X >> 1) * Y;
if (Z & ~(Max >> 1)) {
Overflowed = true;
return Max;
}
Z <<= 1;
if (X & 1)
return SaturatingAdd(Z, Y, ResultOverflowed);
return Z;
}
/// Multiply two unsigned integers, X and Y, and add the unsigned integer, A to
/// the product. Clamp the result to the maximum representable value of T on
/// overflow. ResultOverflowed indicates if the result is larger than the
/// maximum representable value of type T.
template <typename T>
std::enable_if_t<std::is_unsigned_v<T>, T>
SaturatingMultiplyAdd(T X, T Y, T A, bool *ResultOverflowed = nullptr) {
bool Dummy;
bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
T Product = SaturatingMultiply(X, Y, &Overflowed);
if (Overflowed)
return Product;
return SaturatingAdd(A, Product, &Overflowed);
}
/// Use this rather than HUGE_VALF; the latter causes warnings on MSVC.
extern const float huge_valf;
/// Add two signed integers, computing the two's complement truncated result,
/// returning true if overflow occurred.
template <typename T>
std::enable_if_t<std::is_signed_v<T>, T> AddOverflow(T X, T Y, T &Result) {
#if __has_builtin(__builtin_add_overflow)
return __builtin_add_overflow(X, Y, &Result);
#else
// Perform the unsigned addition.
using U = std::make_unsigned_t<T>;
const U UX = static_cast<U>(X);
const U UY = static_cast<U>(Y);
const U UResult = UX + UY;
// Convert to signed.
Result = static_cast<T>(UResult);
// Adding two positive numbers should result in a positive number.
if (X > 0 && Y > 0)
return Result <= 0;
// Adding two negatives should result in a negative number.
if (X < 0 && Y < 0)
return Result >= 0;
return false;
#endif
}
/// Subtract two signed integers, computing the two's complement truncated
/// result, returning true if an overflow ocurred.
template <typename T>
std::enable_if_t<std::is_signed_v<T>, T> SubOverflow(T X, T Y, T &Result) {
#if __has_builtin(__builtin_sub_overflow)
return __builtin_sub_overflow(X, Y, &Result);
#else
// Perform the unsigned addition.
using U = std::make_unsigned_t<T>;
const U UX = static_cast<U>(X);
const U UY = static_cast<U>(Y);
const U UResult = UX - UY;
// Convert to signed.
Result = static_cast<T>(UResult);
// Subtracting a positive number from a negative results in a negative number.
if (X <= 0 && Y > 0)
return Result >= 0;
// Subtracting a negative number from a positive results in a positive number.
if (X >= 0 && Y < 0)
return Result <= 0;
return false;
#endif
}
/// Multiply two signed integers, computing the two's complement truncated
/// result, returning true if an overflow ocurred.
template <typename T>
std::enable_if_t<std::is_signed_v<T>, T> MulOverflow(T X, T Y, T &Result) {
#if __has_builtin(__builtin_mul_overflow)
return __builtin_mul_overflow(X, Y, &Result);
#else
// Perform the unsigned multiplication on absolute values.
using U = std::make_unsigned_t<T>;
const U UX = X < 0 ? (0 - static_cast<U>(X)) : static_cast<U>(X);
const U UY = Y < 0 ? (0 - static_cast<U>(Y)) : static_cast<U>(Y);
const U UResult = UX * UY;
// Convert to signed.
const bool IsNegative = (X < 0) ^ (Y < 0);
Result = IsNegative ? (0 - UResult) : UResult;
// If any of the args was 0, result is 0 and no overflow occurs.
if (UX == 0 || UY == 0)
return false;
// UX and UY are in [1, 2^n], where n is the number of digits.
// Check how the max allowed absolute value (2^n for negative, 2^(n-1) for
// positive) divided by an argument compares to the other.
if (IsNegative)
return UX > (static_cast<U>(std::numeric_limits<T>::max()) + U(1)) / UY;
else
return UX > (static_cast<U>(std::numeric_limits<T>::max())) / UY;
#endif
}
/// Type to force float point values onto the stack, so that x86 doesn't add
/// hidden precision, avoiding rounding differences on various platforms.
#if defined(__i386__) || defined(_M_IX86)
using stack_float_t = volatile float;
#else
using stack_float_t = float;
#endif
} // namespace llvm
#endif

View File

@@ -0,0 +1,24 @@
# LLVM Reference Files
These are a copy of the relevant LLVM files that were ported to Rust from the
last time that this project was "synced" with LLVM.
Currently that sync point is 20.1.8, commit [87f0227](https://github.com/llvm/llvm-project/tree/87f0227cb60147a26a1eeb4fb06e3b505e9c7261).
These files were originally located at:
* `llvm/include/llvm/BinaryFormat/COFF.h`
* `llvm/include/llvm/Object/Archive.h`
* `llvm/include/llvm/Object/ArchiveWriter.h`
* `llvm/include/llvm/Object/COFFImportFile.h`
* `llvm/include/llvm/Support/Alignment.h`
* `llvm/include/llvm/Support/MathExtras.h`
* `llvm/lib/IR/Mangler.cpp`
* `llvm/lib/Object/ArchiveWriter.cpp`
* `llvm/lib/Object/COFFImportFile.cpp`
* `llvm/unittests/IR/ManglerTest.cpp`
When syncing, make sure to update these files and the commit above.
Additionally, `ar_archive_writer` has removed some options, so you can assume:
* `deterministic` is always `true`.
* `write_symtab` is always `true`.

View File

@@ -0,0 +1,3 @@
[toolchain]
channel = "stable"
components = ["llvm-tools"]

View File

@@ -0,0 +1,21 @@
// Derived from code in LLVM, which is:
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
/// Returns a multiple of `align` needed to store `size` bytes.
pub(crate) fn align_to(size: u64, align: u64) -> u64 {
(size + align - 1) & !(align - 1)
}
/*
/// Returns the offset to the next integer (mod 2**64) that is greater than
/// or equal to \p Value and is a multiple of \p Align.
inline uint64_t offsetToAlignment(uint64_t Value, Align Alignment) {
return alignTo(Value, Alignment) - Value;
}
*/
pub(crate) fn offset_to_alignment(value: u64, alignment: u64) -> u64 {
align_to(value, alignment) - value
}

69
vendor/ar_archive_writer/src/archive.rs vendored Normal file
View File

@@ -0,0 +1,69 @@
// Derived from code in LLVM, which is:
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
/// Size field is 10 decimal digits long
pub(crate) const MAX_MEMBER_SIZE: u64 = 9999999999;
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ArchiveKind {
Gnu,
Gnu64,
Bsd,
Darwin,
Darwin64,
Coff,
AixBig,
}
pub(crate) mod big_archive {
#[repr(C)]
pub(crate) struct BigArMemHdrType {
/// File member size in decimal
size: [u8; 20],
/// Next member offset in decimal
next_offset: [u8; 20],
/// Previous member offset in decimal
prev_offset: [u8; 20],
last_modified: [u8; 12],
uid: [u8; 12],
gid: [u8; 12],
access_mode: [u8; 12],
/// File member name length in decimal
name_len: [u8; 4],
terminator: [u8; 2],
}
/// Fixed-Length Header.
#[repr(C)]
pub(crate) struct FixLenHdr {
/// Big archive magic string.
magic: [u8; 8],
/// Offset to member table.
mem_offset: [u8; 20],
/// Offset to global symbol table.
glob_sym_offset: [u8; 20],
/// Offset global symbol table for 64-bit objects.
glob_sym64_offset: [u8; 20],
/// Offset to first archive member.
first_child_offset: [u8; 20],
/// Offset to last archive member.
last_child_offset: [u8; 20],
/// Offset to first mem on free list.
free_offset: [u8; 20],
}
}

File diff suppressed because it is too large Load Diff

94
vendor/ar_archive_writer/src/coff.rs vendored Normal file
View File

@@ -0,0 +1,94 @@
// Derived from code in LLVM, which is:
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[repr(u16)]
#[allow(clippy::upper_case_acronyms)]
pub enum MachineTypes {
AMD64 = object::pe::IMAGE_FILE_MACHINE_AMD64,
ARMNT = object::pe::IMAGE_FILE_MACHINE_ARMNT,
ARM64 = object::pe::IMAGE_FILE_MACHINE_ARM64,
ARM64EC = object::pe::IMAGE_FILE_MACHINE_ARM64EC,
ARM64X = 0xA64E,
I386 = object::pe::IMAGE_FILE_MACHINE_I386,
}
impl From<MachineTypes> for u16 {
fn from(val: MachineTypes) -> Self {
val as u16
}
}
impl TryInto<MachineTypes> for u16 {
type Error = ();
fn try_into(self) -> Result<MachineTypes, Self::Error> {
match self {
object::pe::IMAGE_FILE_MACHINE_AMD64 => Ok(MachineTypes::AMD64),
object::pe::IMAGE_FILE_MACHINE_ARMNT => Ok(MachineTypes::ARMNT),
object::pe::IMAGE_FILE_MACHINE_ARM64 => Ok(MachineTypes::ARM64),
object::pe::IMAGE_FILE_MACHINE_ARM64EC => Ok(MachineTypes::ARM64EC),
0xA64E => Ok(MachineTypes::ARM64X),
object::pe::IMAGE_FILE_MACHINE_I386 => Ok(MachineTypes::I386),
_ => Err(()),
}
}
}
pub fn is_arm64ec(machine: MachineTypes) -> bool {
machine == MachineTypes::ARM64EC || machine == MachineTypes::ARM64X
}
pub fn is_any_arm64(machine: MachineTypes) -> bool {
machine == MachineTypes::ARM64 || is_arm64ec(machine)
}
pub fn is_64_bit(machine: MachineTypes) -> bool {
machine == MachineTypes::AMD64 || is_any_arm64(machine)
}
#[derive(PartialEq, Eq, Copy, Clone)]
#[repr(u16)]
pub enum ImportType {
/// An executable code symbol.
Code = 0,
/// A data symbol.
Data = 1,
/// A constant value.
Const = 2,
}
impl From<ImportType> for u16 {
fn from(val: ImportType) -> Self {
val as u16
}
}
#[derive(PartialEq, Eq, Copy, Clone)]
#[repr(u16)]
pub enum ImportNameType {
/// Import is by ordinal. This indicates that the value in the Ordinal/Hint
/// field of the import header is the import's ordinal. If this constant is
/// not specified, then the Ordinal/Hint field should always be interpreted
/// as the import's hint.
Ordinal = 0,
/// The import name is identical to the public symbol name
Name = 1,
/// The import name is the public symbol name, but skipping the leading ?,
/// @, or optionally _.
NameNoprefix = 2,
/// The import name is the public symbol name, but skipping the leading ?,
/// @, or optionally _, and truncating at the first @.
NameUndecorate = 3,
/// The import name is specified as a separate string in the import library
/// object file.
NameExportas = 4,
}
impl From<ImportNameType> for u16 {
fn from(val: ImportNameType) -> Self {
val as u16
}
}

File diff suppressed because it is too large Load Diff

53
vendor/ar_archive_writer/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,53 @@
#![allow(clippy::too_many_arguments)]
// We are writing a very specific, well defined format, so it makes it easier to
// see exactly what is being written if we explicitly write out `\n` instead of
// hoping somebody notices the `writeln!` instead of `write!`.
#![allow(clippy::write_with_newline)]
mod alignment;
mod archive;
mod archive_writer;
mod coff;
mod coff_import_file;
mod mangler;
mod math_extras;
mod object_reader;
pub use archive::ArchiveKind;
pub use archive_writer::{NewArchiveMember, write_archive_to_stream};
pub use coff::MachineTypes;
pub use coff_import_file::{COFFShortExport, write_import_library};
pub type GetSymbolsFn =
fn(buf: &[u8], f: &mut dyn FnMut(&[u8]) -> std::io::Result<()>) -> std::io::Result<bool>;
pub type Is64BitObjectFileFn = fn(buf: &[u8]) -> bool;
pub type IsECObjectFileFn = fn(buf: &[u8]) -> bool;
pub type IsAnyArm64CoffFn = fn(buf: &[u8]) -> bool;
pub type GetXCoffMemberAlignmentFn = fn(buf: &[u8]) -> u32;
/// Helper struct to query object file information from members.
pub struct ObjectReader {
/// Iterates over the symbols in the object file.
pub get_symbols: GetSymbolsFn,
/// Returns true if the object file is 64-bit.
/// Note that this should match LLVM's `SymbolicFile::is64Bit`, which
/// considers all COFF files to be 32-bit.
pub is_64_bit_object_file: Is64BitObjectFileFn,
/// Returns true if the object file is an EC (that is, an Arm64EC or x64)
/// object file
pub is_ec_object_file: IsECObjectFileFn,
/// Returns true if the object file is any Arm64 (Native Arm64, Arm64EC or
/// Arm64X) COFF file.
pub is_any_arm64_coff: IsAnyArm64CoffFn,
/// Returns the member alignment of an XCoff object file.
pub get_xcoff_member_alignment: GetXCoffMemberAlignmentFn,
}
/// Default implementation of [ObjectReader] that uses the `object` crate.
pub const DEFAULT_OBJECT_READER: ObjectReader = ObjectReader {
get_symbols: object_reader::get_native_object_symbols,
is_64_bit_object_file: object_reader::is_64_bit_symbolic_file,
is_ec_object_file: object_reader::is_ec_object,
is_any_arm64_coff: object_reader::is_any_arm64_coff,
get_xcoff_member_alignment: object_reader::get_member_alignment,
};

44
vendor/ar_archive_writer/src/mangler.rs vendored Normal file
View File

@@ -0,0 +1,44 @@
// Derived from code in LLVM, which is:
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
pub fn get_arm64ec_mangled_function_name(name: &str) -> Result<Option<String>, ()> {
let first_char = name.chars().next().unwrap();
if first_char != '?' {
// For non-C++ symbols, prefix the name with "#" unless it's already
// mangled.
if first_char == '#' {
return Ok(None);
}
return Ok(Some(format!("#{name}")));
}
// If the name contains $$h, then it is already mangled.
if name.contains("$$h") {
return Ok(None);
}
// Ask the demangler where we should insert "$$h".
// TODO: In LLVM, this uses the Microsoft Demangler to demangle the name to
// find the insertion point. The demangler is massive, so rather than
// porting it we're going to require that the name is already mangled.
Err(())
}
pub fn get_arm64ec_demangled_function_name(name: &str) -> Option<String> {
// For non-C++ names, drop the "#" prefix.
let first_char = name.chars().next().unwrap();
if first_char == '#' {
return Some(name[1..].to_string());
}
if first_char != '?' {
return None;
}
// Drop the ARM64EC "$$h" tag.
match name.split_once("$$h") {
Some((first, second)) if !second.is_empty() => Some(format!("{first}{second}")),
_ => None,
}
}

View File

@@ -0,0 +1,15 @@
// Derived from code in LLVM, which is:
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
pub(crate) const fn align_to_power_of2(value: u64, align: u64) -> u64 {
assert!(
align != 0 && (align & (align - 1)) == 0,
"Align must be a power of 2"
);
// Replace unary minus to avoid compilation error on Windows:
// "unary minus operator applied to unsigned type, result still unsigned"
let neg_align = align.wrapping_neg();
(value + align - 1) & neg_align
}

View File

@@ -0,0 +1,209 @@
// Derived from code in LLVM, which is:
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//! Default implementation of [crate::ObjectReader] that uses the `object` crate.
use std::{io, mem::offset_of};
use object::{Object, ObjectSymbol, pe::ImportObjectHeader, xcoff};
use crate::coff::is_any_arm64;
use crate::coff_import_file;
fn is_archive_symbol(sym: &object::read::Symbol<'_, '_>) -> bool {
// FIXME Use a better equivalent of LLVM's SymbolRef::SF_FormatSpecific
if sym.kind() == object::SymbolKind::File || sym.kind() == object::SymbolKind::Section {
return false;
}
if !sym.is_global() {
return false;
}
if sym.is_undefined() {
return false;
}
true
}
pub fn get_native_object_symbols(
buf: &[u8],
f: &mut dyn FnMut(&[u8]) -> io::Result<()>,
) -> io::Result<bool> {
// FIXME match what LLVM does
match object::File::parse(buf) {
Ok(file) => {
for sym in file.symbols() {
if !is_archive_symbol(&sym) {
continue;
}
f(sym.name_bytes().expect("FIXME"))?;
}
Ok(true)
}
Err(_) => {
let mut offset = 0;
// Try to handle this as a COFF import library.
if ImportObjectHeader::parse(buf, &mut offset).is_ok() {
coff_import_file::get_short_import_symbol(buf, f).or(Ok(false))
} else {
Ok(false)
}
}
}
}
pub fn is_ec_object(obj: &[u8]) -> bool {
match object::FileKind::parse(obj) {
Ok(object::FileKind::Coff) => {
u16::from_le_bytes([obj[0], obj[1]]) != object::pe::IMAGE_FILE_MACHINE_ARM64
}
Ok(object::FileKind::CoffImport) => {
// COFF Import Header is:
// sig1: u16
// sig2: u16
// version: u16
// machine: u16
u16::from_le_bytes([obj[6], obj[7]]) != object::pe::IMAGE_FILE_MACHINE_ARM64
}
_ => false,
}
}
pub fn is_any_arm64_coff(obj: &[u8]) -> bool {
match object::FileKind::parse(obj) {
Ok(object::FileKind::Coff) => u16::from_le_bytes([obj[0], obj[1]])
.try_into()
.is_ok_and(is_any_arm64),
Ok(object::FileKind::CoffImport) => {
// COFF Import Header is:
// sig1: u16
// sig2: u16
// version: u16
// machine: u16
u16::from_le_bytes([obj[6], obj[7]])
.try_into()
.is_ok_and(is_any_arm64)
}
_ => false,
}
}
pub fn is_64_bit_symbolic_file(obj: &[u8]) -> bool {
object::FileKind::parse(obj).is_ok_and(|kind| match kind {
object::FileKind::Elf64
| object::FileKind::MachO64
| object::FileKind::Pe64
| object::FileKind::Xcoff64
| object::FileKind::MachOFat64 => true,
object::FileKind::Elf32
| object::FileKind::MachO32
| object::FileKind::Pe32
| object::FileKind::Xcoff32
| object::FileKind::MachOFat32
| object::FileKind::Coff
| object::FileKind::CoffBig
| object::FileKind::CoffImport => false,
_ => panic!("Unexpected file kind"),
})
}
// Log2 of PAGESIZE(4096) on an AIX system.
const LOG2_OF_AIXPAGE_SIZE: u32 = 12;
// In the AIX big archive format, since the data content follows the member file
// name, if the name ends on an odd byte, an extra byte will be added for
// padding. This ensures that the data within the member file starts at an even
// byte.
const MIN_BIG_ARCHIVE_MEM_DATA_ALIGN: u32 = 2;
fn get_aux_max_alignment<AuxiliaryHeader: object::read::xcoff::AuxHeader>(
aux_header_size: u16,
aux_header: Option<&AuxiliaryHeader>,
log_2_of_max_align: u32,
offset_of_modtype: u16,
) -> u32 {
// If the member doesn't have an auxiliary header, it isn't a loadable object
// and so it just needs aligning at the minimum value.
let Some(aux_header) = aux_header else {
return MIN_BIG_ARCHIVE_MEM_DATA_ALIGN;
};
// If the auxiliary header does not have both MaxAlignOfData and
// MaxAlignOfText field, it is not a loadable shared object file, so align at
// the minimum value. The 'ModuleType' member is located right after
// 'MaxAlignOfData' in the AuxiliaryHeader.
if aux_header_size < offset_of_modtype {
return MIN_BIG_ARCHIVE_MEM_DATA_ALIGN;
}
// If the XCOFF object file does not have a loader section, it is not
// loadable, so align at the minimum value.
if aux_header.o_snloader() == 0 {
return MIN_BIG_ARCHIVE_MEM_DATA_ALIGN;
}
// The content of the loadable member file needs to be aligned at MAX(maximum
// alignment of .text, maximum alignment of .data) if there are both fields.
// If the desired alignment is > PAGESIZE, 32-bit members are aligned on a
// word boundary, while 64-bit members are aligned on a PAGESIZE(2^12=4096)
// boundary.
let log_2_of_align = u32::from(std::cmp::max(
aux_header.o_algntext(),
aux_header.o_algndata(),
));
1 << (if log_2_of_align > LOG2_OF_AIXPAGE_SIZE {
log_2_of_max_align
} else {
log_2_of_align
})
}
// AIX big archives may contain shared object members. The AIX OS requires these
// members to be aligned if they are 64-bit and recommends it for 32-bit
// members. This ensures that when these members are loaded they are aligned in
// memory.
pub fn get_member_alignment(obj: &[u8]) -> u32 {
use object::read::xcoff::FileHeader;
// If the desired alignment is > PAGESIZE, 32-bit members are aligned on a
// word boundary, while 64-bit members are aligned on a PAGESIZE boundary.
match object::FileKind::parse(obj) {
Ok(object::FileKind::Xcoff64) => {
let mut offset = 0;
let Ok(header) = xcoff::FileHeader64::parse(obj, &mut offset) else {
return MIN_BIG_ARCHIVE_MEM_DATA_ALIGN;
};
let Ok(aux_header) = header.aux_header(obj, &mut offset) else {
return MIN_BIG_ARCHIVE_MEM_DATA_ALIGN;
};
get_aux_max_alignment(
header.f_opthdr(),
aux_header,
LOG2_OF_AIXPAGE_SIZE,
offset_of!(object::xcoff::AuxHeader64, o_modtype)
.try_into()
.unwrap(),
)
}
Ok(object::FileKind::Xcoff32) => {
let mut offset = 0;
let Ok(header) = object::xcoff::FileHeader32::parse(obj, &mut offset) else {
return MIN_BIG_ARCHIVE_MEM_DATA_ALIGN;
};
let Ok(aux_header) = header.aux_header(obj, &mut offset) else {
return MIN_BIG_ARCHIVE_MEM_DATA_ALIGN;
};
get_aux_max_alignment(
header.f_opthdr(),
aux_header,
2,
offset_of!(object::xcoff::AuxHeader32, o_modtype)
.try_into()
.unwrap(),
)
}
_ => MIN_BIG_ARCHIVE_MEM_DATA_ALIGN,
}
}

393
vendor/ar_archive_writer/tests/common.rs vendored Normal file
View File

@@ -0,0 +1,393 @@
#![allow(dead_code)]
use std::fs;
use std::io::Cursor;
use std::path::{Path, PathBuf};
use std::process::Command;
use ar_archive_writer::{ArchiveKind, NewArchiveMember};
use object::write::{self, Object};
use object::{
Architecture, BinaryFormat, Endianness, SubArchitecture, SymbolFlags, SymbolKind, SymbolScope,
};
use pretty_assertions::assert_eq;
/// Creates the temporary directory for a test.
pub fn create_tmp_dir(test_name: &str) -> PathBuf {
let tmpdir = PathBuf::from(env!("CARGO_TARGET_TMPDIR")).join(test_name);
match fs::remove_dir_all(&tmpdir) {
Ok(_) => {}
Err(err) => {
if err.kind() != std::io::ErrorKind::NotFound {
panic!("Failed to delete directory: {tmpdir:?}");
}
}
}
fs::create_dir_all(&tmpdir).unwrap();
tmpdir
}
/// Creates a symlink to `llvm-ar` so that it acts like `llvm-lib`.
pub fn create_llvm_lib_tool(tmp_dir: &Path) -> PathBuf {
let ar_path = cargo_binutils::Tool::Ar.path().unwrap();
let lib_path = tmp_dir.join("llvm-lib");
#[cfg(unix)]
std::os::unix::fs::symlink(ar_path, &lib_path).unwrap();
#[cfg(windows)]
std::os::windows::fs::symlink_file(ar_path, &lib_path).unwrap();
lib_path
}
/// Creates a symlink to `llvm-ar` so that it acts like `llvm-dlltool`.
pub fn create_llvm_dlltool_tool(tmp_dir: &Path) -> PathBuf {
let ar_path = cargo_binutils::Tool::Ar.path().unwrap();
let lib_path = tmp_dir.join("llvm-dlltool");
#[cfg(unix)]
std::os::unix::fs::symlink(ar_path, &lib_path).unwrap();
#[cfg(windows)]
std::os::windows::fs::symlink_file(ar_path, &lib_path).unwrap();
lib_path
}
fn run_llvm_ar(
object_paths: &[PathBuf],
archive_path: &Path,
archive_kind: ArchiveKind,
thin: bool,
is_ec: bool,
) {
// FIXME: LLVM 19 adds support for "coff" as a format argument, so in the
// meantime, we'll instruct llvm-ar to pretend to be llvm-lib.
let output = if archive_kind == ArchiveKind::Coff {
let lib_path = create_llvm_lib_tool(archive_path.parent().unwrap());
let mut command = Command::new(lib_path);
if is_ec {
command.arg("/machine:arm64ec");
}
// llvm-lib reverses the order of the files versus llvm-ar.
let mut object_paths = Vec::from(object_paths);
object_paths.reverse();
command
.arg("/OUT:".to_string() + archive_path.to_str().unwrap())
.args(object_paths)
.output()
.unwrap()
} else {
let ar_path = cargo_binutils::Tool::Ar.path().unwrap();
let mut command = Command::new(ar_path);
let format_arg = match archive_kind {
ArchiveKind::AixBig => "bigarchive",
ArchiveKind::Darwin => "darwin",
ArchiveKind::Gnu => "gnu",
_ => panic!("unsupported archive kind: {archive_kind:?}"),
};
command.arg(format!("--format={format_arg}"));
if thin {
command.arg("--thin");
}
command
.arg("rcs")
.arg(archive_path)
.args(object_paths)
.output()
.unwrap()
};
assert_eq!(
String::from_utf8_lossy(&output.stderr),
"",
"llvm-ar failed. archive: {archive_path:?}"
);
}
/// Creates an archive with the given objects using `llvm-ar`.
/// The generated archive is written to disk as `output_llvm_ar.a`.
pub fn create_archive_with_llvm_ar<'name, 'data>(
tmpdir: &Path,
archive_kind: ArchiveKind,
input_objects: impl IntoIterator<Item = (&'name str, &'data [u8])>,
thin: bool,
is_ec: bool,
) -> Vec<u8> {
let archive_file_path = tmpdir.join("output_llvm_ar.a");
let input_file_paths = input_objects
.into_iter()
.map(|(name, bytes)| {
let input_file_path = tmpdir.join(name);
if name.contains('/') {
fs::create_dir_all(input_file_path.parent().unwrap()).unwrap();
}
fs::write(&input_file_path, bytes).unwrap();
input_file_path
})
.collect::<Vec<_>>();
run_llvm_ar(
&input_file_paths,
&archive_file_path,
archive_kind,
thin,
is_ec,
);
fs::read(archive_file_path).unwrap()
}
/// Creates an archive with the given objects using `ar_archive_writer`.
/// The generated archive is written to disk as `output_ar_archive_writer.a`.
pub fn create_archive_with_ar_archive_writer<'name, 'data>(
tmpdir: &Path,
archive_kind: ArchiveKind,
input_objects: impl IntoIterator<Item = (&'name str, &'data [u8])>,
thin: bool,
is_ec: bool,
) -> Vec<u8> {
let members = input_objects
.into_iter()
.map(|(name, bytes)| {
let member_name = if thin {
// Thin archives use the full path to the object file.
tmpdir
.join(name)
.to_string_lossy()
.replace(std::path::MAIN_SEPARATOR, "/")
} else if archive_kind == ArchiveKind::Coff {
// For COFF archives, we are running llvm-ar as lib.exe, which
// uses the full path to the object file.
tmpdir.join(name).to_string_lossy().to_string()
} else {
// Non-thin archives use the file name only.
name.rsplit_once('/')
.map_or(name, |(_, filename)| filename)
.to_string()
};
NewArchiveMember::new(
bytes,
&ar_archive_writer::DEFAULT_OBJECT_READER,
member_name,
)
})
.collect::<Vec<_>>();
let mut output_bytes = Cursor::new(Vec::new());
ar_archive_writer::write_archive_to_stream(
&mut output_bytes,
&members,
archive_kind,
thin,
Some(is_ec),
)
.unwrap();
let output_archive_bytes = output_bytes.into_inner();
let ar_archive_writer_file_path = tmpdir.join("output_ar_archive_writer.a");
fs::write(ar_archive_writer_file_path, &output_archive_bytes).unwrap();
output_archive_bytes
}
/// Helper for comparing archives generated by `llvm-ar` and `ar_archive_writer`
/// across a variety of archive kinds and their relevant object formats.
pub fn generate_archive_and_compare<F>(test_name: &str, generate_objects: F)
where
F: Fn(
Architecture,
Option<SubArchitecture>,
Endianness,
BinaryFormat,
) -> Vec<(&'static str, Vec<u8>)>,
{
for (architecture, subarch, endianness, binary_format, archive_kind, thin) in [
// Elf + GNU + non-thin
(
Architecture::X86_64,
None,
Endianness::Little,
BinaryFormat::Elf,
ArchiveKind::Gnu,
false,
),
(
Architecture::I386,
None,
Endianness::Little,
BinaryFormat::Elf,
ArchiveKind::Gnu,
false,
),
(
Architecture::Aarch64,
None,
Endianness::Little,
BinaryFormat::Elf,
ArchiveKind::Gnu,
false,
),
// Elf + GNU + thin
(
Architecture::X86_64,
None,
Endianness::Little,
BinaryFormat::Elf,
ArchiveKind::Gnu,
true,
),
(
Architecture::I386,
None,
Endianness::Little,
BinaryFormat::Elf,
ArchiveKind::Gnu,
true,
),
(
Architecture::Aarch64,
None,
Endianness::Little,
BinaryFormat::Elf,
ArchiveKind::Gnu,
true,
),
// AIX Big
(
Architecture::PowerPc64,
None,
Endianness::Big,
BinaryFormat::Elf,
ArchiveKind::AixBig,
false,
),
// PE + GNU
(
Architecture::X86_64,
None,
Endianness::Little,
BinaryFormat::Coff,
ArchiveKind::Gnu,
false,
),
(
Architecture::I386,
None,
Endianness::Little,
BinaryFormat::Coff,
ArchiveKind::Gnu,
false,
),
// PE + Coff
(
Architecture::X86_64,
None,
Endianness::Little,
BinaryFormat::Coff,
ArchiveKind::Coff,
false,
),
(
Architecture::I386,
None,
Endianness::Little,
BinaryFormat::Coff,
ArchiveKind::Coff,
false,
),
(
Architecture::Aarch64,
None,
Endianness::Little,
BinaryFormat::Coff,
ArchiveKind::Coff,
false,
),
(
Architecture::Aarch64,
Some(SubArchitecture::Arm64EC),
Endianness::Little,
BinaryFormat::Coff,
ArchiveKind::Coff,
false,
),
// MachO
(
Architecture::X86_64,
None,
Endianness::Little,
BinaryFormat::MachO,
ArchiveKind::Darwin,
false,
),
(
Architecture::Aarch64,
None,
Endianness::Little,
BinaryFormat::MachO,
ArchiveKind::Darwin,
false,
),
(
Architecture::Aarch64,
Some(SubArchitecture::Arm64E),
Endianness::Little,
BinaryFormat::MachO,
ArchiveKind::Darwin,
false,
),
] {
let is_ec = subarch == Some(SubArchitecture::Arm64EC);
let tmpdir = create_tmp_dir(test_name);
let input_objects = generate_objects(architecture, subarch, endianness, binary_format);
let llvm_ar_archive = create_archive_with_llvm_ar(
&tmpdir,
archive_kind,
input_objects
.iter()
.map(|(name, bytes)| (*name, bytes.as_slice())),
thin,
is_ec,
);
let ar_archive_writer_archive = create_archive_with_ar_archive_writer(
&tmpdir,
archive_kind,
input_objects
.iter()
.map(|(name, bytes)| (*name, bytes.as_slice())),
thin,
is_ec,
);
assert_eq!(
llvm_ar_archive, ar_archive_writer_archive,
"Archives differ for architecture: {architecture:?}, binary_format: {binary_format:?}, archive_kind: {archive_kind:?}, thin: {thin}",
);
}
}
pub fn add_file_with_functions_to_object(
object: &mut Object<'_>,
file_name: &[u8],
func_names: &[&[u8]],
) {
object.add_file_symbol(file_name.to_vec());
let text = object.section_id(write::StandardSection::Text);
object.append_section_data(text, &[1; 30], 4);
for func_name in func_names {
let offset = object.append_section_data(text, &[1; 30], 4);
object.add_symbol(write::Symbol {
name: func_name.to_vec(),
value: offset,
size: 32,
kind: SymbolKind::Text,
scope: SymbolScope::Linkage,
weak: false,
section: write::SymbolSection::Section(text),
flags: SymbolFlags::None,
});
}
}

View File

@@ -0,0 +1,63 @@
; This must match get_members() in import_library.rs
LIBRARY MyLibrary
EXPORTS
NormalFunc
NormalData DATA
NormalConstant CONSTANT
PrivateFunc PRIVATE
FuncWithOrdinal @1
FuncWithNoName @2 NONAME
RenamedFunc=InternalName
ReexportedFunc=OtherModule.OtherName
ReexportedViaOrd=OtherModule.#42
FuncWithImportName==ImportName
; These are Arm64EC mangled names, since we currently don't support converting
; the non-Arm64EC format to Arm64EC. They are taken from ManglerTest.cpp
; Basic C++ name.
?foo@@$$hYAHXZ
; Regression test: https://github.com/llvm/llvm-project/issues/115231
?GetValue@?$Wrapper@UA@@@@$$hQEBAHXZ
; Symbols from:
; ```
; namespace A::B::C::D {
; struct Base {
; virtual int f() { return 0; }
; };
; }
; struct Derived : public A::B::C::D::Base {
; virtual int f() override { return 1; }
; };
; A::B::C::D::Base* MakeObj() { return new Derived(); }
; ```
; void * __cdecl operator new(unsigned __int64)
??2@$$hYAPEAX_K@Z
; public: virtual int __cdecl A::B::C::D::Base::f(void)
?f@Base@D@C@B@A@@$$hUEAAHXZ
; public: __cdecl A::B::C::D::Base::Base(void)
??0Base@D@C@B@A@@$$hQEAA@XZ
; public: virtual int __cdecl Derived::f(void)
?f@Derived@@$$hUEAAHXZ
; public: __cdecl Derived::Derived(void)
??0Derived@@$$hQEAA@XZ
; struct A::B::C::D::Base * __cdecl MakeObj(void)
?MakeObj@@$$hYAPEAUBase@D@C@B@A@@XZ
; Symbols from:
; ```
; template <typename T> struct WW { struct Z{}; };
; template <typename X> struct Wrapper {
; int GetValue(typename WW<X>::Z) const;
; };
; struct A { };
; template <typename X> int Wrapper<X>::GetValue(typename WW<X>::Z) const
; { return 3; }
; template class Wrapper<A>;
; ```
; public: int __cdecl Wrapper<struct A>::GetValue(struct WW<struct
; A>::Z)const
?GetValue@?$Wrapper@UA@@@@$$hQEBAHUZ@?$WW@UA@@@@@Z

View File

@@ -0,0 +1,387 @@
use std::fs;
use std::io::Cursor;
use std::path::{Path, PathBuf};
use std::process::Command;
use ar_archive_writer::{ArchiveKind, COFFShortExport, MachineTypes};
use common::{create_archive_with_ar_archive_writer, create_archive_with_llvm_ar};
use object::coff::CoffFile;
use object::pe::ImageFileHeader;
use object::read::archive::ArchiveFile;
use object::{Architecture, Object, ObjectSection, ObjectSymbol, SubArchitecture, bytes_of};
use pretty_assertions::assert_eq;
mod common;
const DEFAULT_EXPORT: COFFShortExport = COFFShortExport {
name: String::new(),
ext_name: None,
symbol_name: None,
export_as: None,
import_name: None,
ordinal: 0,
noname: false,
data: false,
private: false,
constant: false,
};
fn get_members(machine_type: MachineTypes) -> Vec<COFFShortExport> {
let prefix = match machine_type {
MachineTypes::I386 => "_",
_ => "",
};
// This must match import_library.def.
vec![
COFFShortExport {
name: format!("{prefix}NormalFunc"),
..DEFAULT_EXPORT
},
COFFShortExport {
name: format!("{prefix}NormalData"),
data: true,
..DEFAULT_EXPORT
},
COFFShortExport {
name: format!("{prefix}NormalConstant"),
constant: true,
..DEFAULT_EXPORT
},
COFFShortExport {
name: format!("{prefix}PrivateFunc"),
private: true,
..DEFAULT_EXPORT
},
COFFShortExport {
name: format!("{prefix}FuncWithOrdinal"),
ordinal: 1,
..DEFAULT_EXPORT
},
COFFShortExport {
name: format!("{prefix}FuncWithNoName"),
ordinal: 2,
noname: true,
..DEFAULT_EXPORT
},
COFFShortExport {
name: format!("{prefix}InternalName"),
ext_name: Some(format!("{prefix}RenamedFunc")),
..DEFAULT_EXPORT
},
COFFShortExport {
name: format!("{prefix}OtherModule.OtherName"),
ext_name: Some(format!("{prefix}ReexportedFunc")),
..DEFAULT_EXPORT
},
COFFShortExport {
name: format!("{prefix}OtherModule.#42"),
ext_name: Some(format!("{prefix}ReexportedViaOrd")),
..DEFAULT_EXPORT
},
COFFShortExport {
name: format!("{prefix}FuncWithImportName"),
import_name: Some("ImportName".to_string()),
..DEFAULT_EXPORT
},
COFFShortExport {
name: "?foo@@$$hYAHXZ".to_string(),
..DEFAULT_EXPORT
},
COFFShortExport {
name: "?GetValue@?$Wrapper@UA@@@@$$hQEBAHXZ".to_string(),
..DEFAULT_EXPORT
},
COFFShortExport {
name: "??2@$$hYAPEAX_K@Z".to_string(),
..DEFAULT_EXPORT
},
COFFShortExport {
name: "?f@Base@D@C@B@A@@$$hUEAAHXZ".to_string(),
..DEFAULT_EXPORT
},
COFFShortExport {
name: "??0Base@D@C@B@A@@$$hQEAA@XZ".to_string(),
..DEFAULT_EXPORT
},
COFFShortExport {
name: "?f@Derived@@$$hUEAAHXZ".to_string(),
..DEFAULT_EXPORT
},
COFFShortExport {
name: "??0Derived@@$$hQEAA@XZ".to_string(),
..DEFAULT_EXPORT
},
COFFShortExport {
name: "?MakeObj@@$$hYAPEAUBase@D@C@B@A@@XZ".to_string(),
..DEFAULT_EXPORT
},
COFFShortExport {
name: "?GetValue@?$Wrapper@UA@@@@$$hQEBAHUZ@?$WW@UA@@@@@Z".to_string(),
..DEFAULT_EXPORT
},
]
}
fn create_import_library_with_ar_archive_writer(
temp_dir: &Path,
machine_type: MachineTypes,
mingw: bool,
comdat: bool,
) -> Vec<u8> {
let mut output_bytes = Cursor::new(Vec::new());
ar_archive_writer::write_import_library(
&mut output_bytes,
"MyLibrary.dll",
&get_members(machine_type),
machine_type,
mingw,
comdat,
&[],
)
.unwrap();
let output_archive_bytes = output_bytes.into_inner();
let ar_archive_writer_file_path = temp_dir.join("output_ar_archive_writer.a");
fs::write(ar_archive_writer_file_path, &output_archive_bytes).unwrap();
output_archive_bytes
}
#[test]
fn compare_to_lib() {
for machine_type in [
MachineTypes::I386,
MachineTypes::AMD64,
MachineTypes::ARMNT,
MachineTypes::ARM64,
MachineTypes::ARM64EC,
] {
let temp_dir = common::create_tmp_dir("import_library_compare_to_lib");
let archive_writer_bytes =
create_import_library_with_ar_archive_writer(&temp_dir, machine_type, false, false);
let llvm_lib_bytes = {
let machine_arg = match machine_type {
MachineTypes::I386 => "X86",
MachineTypes::AMD64 => "X64",
MachineTypes::ARMNT => "ARM",
MachineTypes::ARM64 => "ARM64",
MachineTypes::ARM64EC => "ARM64EC",
_ => panic!("Unsupported machine type"),
};
let llvm_lib_tool_path = common::create_llvm_lib_tool(&temp_dir);
let output_library_path = temp_dir.join("output_llvm_lib.a");
let def_path =
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/import_library.def");
let output = Command::new(llvm_lib_tool_path)
.arg(format!("/MACHINE:{machine_arg}"))
.arg(format!("/DEF:{}", def_path.to_string_lossy()))
.arg(format!("/OUT:{}", output_library_path.to_string_lossy()))
.output()
.unwrap();
assert_eq!(
String::from_utf8_lossy(&output.stderr),
"",
"llvm-lib failed. archive: {output_library_path:?}"
);
fs::read(output_library_path).unwrap()
};
assert_eq!(
llvm_lib_bytes, archive_writer_bytes,
"Import library differs. Machine type: {machine_type:?}",
);
compare_comdat(
&create_import_library_with_ar_archive_writer(&temp_dir, machine_type, false, true),
&llvm_lib_bytes,
);
}
}
#[test]
fn compare_to_dlltool() {
for machine_type in [
MachineTypes::I386,
MachineTypes::AMD64,
MachineTypes::ARMNT,
MachineTypes::ARM64,
] {
let temp_dir = common::create_tmp_dir("import_library_compare_to_dlltool");
let archive_writer_bytes =
create_import_library_with_ar_archive_writer(&temp_dir, machine_type, true, false);
let llvm_lib_bytes = {
let machine_arg = match machine_type {
MachineTypes::I386 => "i386",
MachineTypes::AMD64 => "i386:x86-64",
MachineTypes::ARMNT => "arm",
MachineTypes::ARM64 => "arm64",
_ => panic!("Unsupported machine type"),
};
let llvm_lib_tool_path = common::create_llvm_dlltool_tool(&temp_dir);
let output_library_path = temp_dir.join("output_llvm_lib.a");
let def_path =
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/import_library.def");
let output = Command::new(llvm_lib_tool_path)
.arg("--machine")
.arg(machine_arg)
.arg("--input-def")
.arg(def_path)
.arg("--output-lib")
.arg(&output_library_path)
.output()
.unwrap();
assert_eq!(
String::from_utf8_lossy(&output.stderr),
"",
"llvm-lib failed. archive: {output_library_path:?}"
);
fs::read(output_library_path).unwrap()
};
assert_eq!(
llvm_lib_bytes, archive_writer_bytes,
"Import library differs. Machine type: {machine_type:?}",
);
compare_comdat(
&create_import_library_with_ar_archive_writer(&temp_dir, machine_type, true, true),
&llvm_lib_bytes,
);
}
}
/// Creates an import library and then wraps that in an archive.
#[test]
fn wrap_in_archive() {
for (architecture, subarch, machine_type) in [
(Architecture::I386, None, MachineTypes::I386),
(Architecture::X86_64, None, MachineTypes::AMD64),
(Architecture::Arm, None, MachineTypes::ARMNT),
(Architecture::Aarch64, None, MachineTypes::ARM64),
(
Architecture::Aarch64,
Some(SubArchitecture::Arm64EC),
MachineTypes::ARM64EC,
),
] {
let temp_dir = common::create_tmp_dir("import_library_wrap_in_archive");
let mut import_lib_bytes = Cursor::new(Vec::new());
ar_archive_writer::write_import_library(
&mut import_lib_bytes,
&temp_dir.join("MyLibrary.dll").to_string_lossy(),
&get_members(machine_type),
machine_type,
false,
false,
&[],
)
.unwrap();
let import_lib_bytes = import_lib_bytes.into_inner();
let is_ec = subarch == Some(SubArchitecture::Arm64EC);
let llvm_ar_archive = create_archive_with_llvm_ar(
&temp_dir,
ArchiveKind::Coff,
[("MyLibrary.dll.lib", import_lib_bytes.as_slice())],
false,
is_ec,
);
// When a archive is passed into lib.exe, it is opened and the individual members are included
// in the new output library. Also, for whatever reason, the members are reversed.
let archive = ArchiveFile::parse(import_lib_bytes.as_slice()).unwrap();
let mut members = archive
.members()
.map(|m| {
let member = m.unwrap();
(
String::from_utf8(member.name().to_vec()).unwrap(),
member.data(import_lib_bytes.as_slice()).unwrap(),
)
})
.collect::<Vec<_>>();
members.reverse();
let ar_archive_writer_archive = create_archive_with_ar_archive_writer(
&temp_dir,
ArchiveKind::Coff,
members.iter().map(|(name, data)| (name.as_str(), *data)),
false,
is_ec,
);
assert_eq!(
llvm_ar_archive, ar_archive_writer_archive,
"Archives differ for architecture: {architecture:?}, subarch: {subarch:?}, machine type: {machine_type:?}",
);
}
}
fn compare_comdat(archive_writer_bytes: &[u8], llvm_bytes: &[u8]) {
let archive_writer = ArchiveFile::parse(archive_writer_bytes).unwrap();
let llvm = ArchiveFile::parse(llvm_bytes).unwrap();
for (archive_member, llvm_member) in archive_writer.members().zip(llvm.members()) {
let archive_member = archive_member.unwrap();
let llvm_member = llvm_member.unwrap();
// skip the EC symbols table.
if archive_member.name() == b"/<ECSYMBOLS>/" {
continue;
}
if archive_member.size() != llvm_member.size() {
// Ensure that the member header is the same except for the file size.
let mut llvm_file_header = *llvm_member.header().unwrap();
llvm_file_header.size = archive_member.header().unwrap().size;
assert_eq!(
bytes_of(archive_member.header().unwrap()),
bytes_of(&llvm_file_header)
);
// Make sure they are both COFF files with the same sections and symbols,
// except for the different naming for the null import descriptor.
let archive_data = archive_member.data(archive_writer_bytes).unwrap();
let llvm_data = llvm_member.data(llvm_bytes).unwrap();
let archive_file = CoffFile::<_, ImageFileHeader>::parse(archive_data).unwrap();
let llvm_file = CoffFile::<_, ImageFileHeader>::parse(llvm_data).unwrap();
for (archive_section, llvm_section) in archive_file.sections().zip(llvm_file.sections())
{
assert_eq!(archive_section.data(), llvm_section.data());
}
for (archive_symbol, llvm_symbol) in archive_file.symbols().zip(llvm_file.symbols()) {
if llvm_symbol.name().unwrap() == "__NULL_IMPORT_DESCRIPTOR" {
assert!(
archive_symbol
.name()
.unwrap()
.starts_with("__NULL_IMPORT_DESCRIPTOR_")
);
} else {
assert_eq!(archive_symbol.name(), llvm_symbol.name());
}
let archive_coff_symbol = archive_symbol.coff_symbol();
let mut llvm_coff_symbol = *llvm_symbol.coff_symbol();
llvm_coff_symbol.name = archive_coff_symbol.name;
assert_eq!(bytes_of(archive_coff_symbol), bytes_of(&llvm_coff_symbol));
}
} else {
assert_eq!(
bytes_of(archive_member.header().unwrap()),
bytes_of(llvm_member.header().unwrap())
);
assert_eq!(
archive_member.data(archive_writer_bytes).unwrap(),
llvm_member.data(llvm_bytes).unwrap()
);
}
}
}

View File

@@ -0,0 +1,59 @@
use object::write;
mod common;
/// Tests creating an archive with multiple objects.
/// Note that `func_overlapping` exists in both objects - this is to test
/// deduplication of symbols in the symbol table (where supported).
#[test]
fn basic_multiple_objects() {
common::generate_archive_and_compare(
"basic_multiple_objects",
|architecture, subarch, endianness, binary_format| {
let mut object1 = write::Object::new(binary_format, architecture, endianness);
object1.set_sub_architecture(subarch);
common::add_file_with_functions_to_object(
&mut object1,
b"file1.c",
&[b"func1", b"func2", b"func_overlapping"],
);
let mut object2 = write::Object::new(binary_format, architecture, endianness);
object2.set_sub_architecture(subarch);
common::add_file_with_functions_to_object(
&mut object2,
b"file2.c",
&[b"func3", b"func4", b"func_overlapping"],
);
vec![
("file1.o", object1.write().unwrap()),
("file2.o", object2.write().unwrap()),
]
},
);
}
/// Tests creating an archive with multiple objects with the same name.
/// This is important for Mach), which uses the timestamp when in deterministic
/// mode to differentiate the two objects.
#[test]
fn multiple_objects_same_name() {
common::generate_archive_and_compare(
"multiple_objects_same_name",
|architecture, subarch, endianness, binary_format| {
let mut object1 = write::Object::new(binary_format, architecture, endianness);
object1.set_sub_architecture(subarch);
common::add_file_with_functions_to_object(&mut object1, b"file1.c", &[b"func1"]);
let mut object2 = write::Object::new(binary_format, architecture, endianness);
object2.set_sub_architecture(subarch);
common::add_file_with_functions_to_object(&mut object2, b"file2.c", &[b"func2"]);
vec![
("1/file.o", object1.write().unwrap()),
("2/file.o", object2.write().unwrap()),
]
},
);
}

View File

@@ -0,0 +1,467 @@
// Derived from object's round_trip.rs:
// https://github.com/gimli-rs/object/blob/0.35.0/tests/round_trip/mod.rs
use ar_archive_writer::ArchiveKind;
use object::{
Architecture, BinaryFormat, Endianness, RelocationEncoding, RelocationKind, SymbolFlags,
SymbolKind, SymbolScope,
};
use object::{RelocationFlags, SubArchitecture, read, write};
use pretty_assertions::assert_eq;
mod common;
fn round_trip_and_diff(
test_name: &str,
object: write::Object<'_>,
archive_kind: ArchiveKind,
trim_output_bytes: usize,
) {
let tmpdir = common::create_tmp_dir(test_name);
let input_bytes = object.write().unwrap();
let is_ec = object.sub_architecture() == Some(SubArchitecture::Arm64EC);
// Create a new archive using ar_archive_writer.
let output_archive_bytes = common::create_archive_with_ar_archive_writer(
&tmpdir,
archive_kind,
[("input.o", input_bytes.as_slice())],
false,
is_ec,
);
// Use llvm-ar to create the archive and diff with ar_archive_writer.
let output_llvm_ar_bytes = common::create_archive_with_llvm_ar(
&tmpdir,
archive_kind,
[("input.o", input_bytes.as_slice())],
false,
is_ec,
);
assert_eq!(
output_archive_bytes, output_llvm_ar_bytes,
"Comparing ar_archive_writer to llvm-ar. Test case: build {:?} for {:?}",
archive_kind, object
);
// Read the archive and member using object and diff with original data.
{
let output_archive =
read::archive::ArchiveFile::parse(output_archive_bytes.as_slice()).unwrap();
let mut members = output_archive.members();
let output_member = members.next().unwrap().unwrap();
if archive_kind != ArchiveKind::Coff {
assert_eq!(output_member.name(), b"input.o");
}
let output_bytes = output_member.data(output_archive_bytes.as_slice()).unwrap();
// Apply fixup if required.
let output_bytes = &output_bytes[..output_bytes.len() - trim_output_bytes];
assert_eq!(
&input_bytes, output_bytes,
"Comparing object after round-trip. Test case: build {:?} for {:?}",
archive_kind, object
);
}
}
#[test]
fn coff_any() {
for (arch, sub_arch) in [
(Architecture::Aarch64, None),
(Architecture::Aarch64, Some(SubArchitecture::Arm64EC)),
(Architecture::Arm, None),
(Architecture::I386, None),
(Architecture::X86_64, None),
]
.iter()
.copied()
{
for archive_kind in [ArchiveKind::Gnu, ArchiveKind::Coff] {
let mut object = write::Object::new(BinaryFormat::Coff, arch, Endianness::Little);
object.set_sub_architecture(sub_arch);
object.add_file_symbol(b"file.c".to_vec());
let text = object.section_id(write::StandardSection::Text);
object.append_section_data(text, &[1; 30], 4);
let func1_offset = object.append_section_data(text, &[1; 30], 4);
assert_eq!(func1_offset, 32);
let func1_symbol = object.add_symbol(write::Symbol {
name: b"func1".to_vec(),
value: func1_offset,
size: 32,
kind: SymbolKind::Text,
scope: SymbolScope::Linkage,
weak: false,
section: write::SymbolSection::Section(text),
flags: SymbolFlags::None,
});
let func2_offset = object.append_section_data(text, &[1; 30], 4);
assert_eq!(func2_offset, 64);
object.add_symbol(write::Symbol {
name: b"func2_long".to_vec(),
value: func2_offset,
size: 32,
kind: SymbolKind::Text,
scope: SymbolScope::Linkage,
weak: false,
section: write::SymbolSection::Section(text),
flags: SymbolFlags::None,
});
object
.add_relocation(
text,
write::Relocation {
offset: 8,
symbol: func1_symbol,
addend: 0,
flags: RelocationFlags::Generic {
kind: RelocationKind::Absolute,
encoding: RelocationEncoding::Generic,
size: arch.address_size().unwrap().bytes() * 8,
},
},
)
.unwrap();
round_trip_and_diff("coff_any", object, archive_kind, 0);
}
}
}
#[test]
fn elf_x86_64() {
let mut object =
write::Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little);
object.add_file_symbol(b"file.c".to_vec());
let text = object.section_id(write::StandardSection::Text);
object.append_section_data(text, &[1; 30], 4);
let func1_offset = object.append_section_data(text, &[1; 30], 4);
assert_eq!(func1_offset, 32);
let func1_symbol = object.add_symbol(write::Symbol {
name: b"func1".to_vec(),
value: func1_offset,
size: 32,
kind: SymbolKind::Text,
scope: SymbolScope::Linkage,
weak: false,
section: write::SymbolSection::Section(text),
flags: SymbolFlags::None,
});
object
.add_relocation(
text,
write::Relocation {
offset: 8,
symbol: func1_symbol,
addend: 0,
flags: RelocationFlags::Generic {
kind: RelocationKind::Absolute,
encoding: RelocationEncoding::Generic,
size: 64,
},
},
)
.unwrap();
round_trip_and_diff("elf_x86_64", object, ArchiveKind::Gnu, 0);
}
#[test]
fn elf_any() {
for (arch, endian, archive_kinds) in [
(
Architecture::Aarch64,
Endianness::Little,
&[ArchiveKind::Gnu][..],
),
(
Architecture::Aarch64_Ilp32,
Endianness::Little,
&[ArchiveKind::Gnu],
),
(Architecture::Arm, Endianness::Little, &[ArchiveKind::Gnu]),
(Architecture::Avr, Endianness::Little, &[ArchiveKind::Gnu]),
(Architecture::Bpf, Endianness::Little, &[ArchiveKind::Gnu]),
(Architecture::Csky, Endianness::Little, &[ArchiveKind::Gnu]),
(Architecture::I386, Endianness::Little, &[ArchiveKind::Gnu]),
(
Architecture::X86_64,
Endianness::Little,
&[ArchiveKind::Gnu],
),
(
Architecture::X86_64_X32,
Endianness::Little,
&[ArchiveKind::Gnu],
),
(
Architecture::Hexagon,
Endianness::Little,
&[ArchiveKind::Gnu],
),
(
Architecture::LoongArch64,
Endianness::Little,
&[ArchiveKind::Gnu],
),
(Architecture::Mips, Endianness::Little, &[ArchiveKind::Gnu]),
(
Architecture::Mips64,
Endianness::Little,
&[ArchiveKind::Gnu],
),
(
Architecture::Msp430,
Endianness::Little,
&[ArchiveKind::Gnu],
),
(Architecture::PowerPc, Endianness::Big, &[ArchiveKind::Gnu]),
(
Architecture::PowerPc64,
Endianness::Big,
&[ArchiveKind::Gnu, ArchiveKind::AixBig],
),
(
Architecture::Riscv32,
Endianness::Little,
&[ArchiveKind::Gnu],
),
(
Architecture::Riscv64,
Endianness::Little,
&[ArchiveKind::Gnu],
),
(Architecture::S390x, Endianness::Big, &[ArchiveKind::Gnu]),
(Architecture::Sbf, Endianness::Little, &[ArchiveKind::Gnu]),
(Architecture::Sparc64, Endianness::Big, &[ArchiveKind::Gnu]),
(
Architecture::Xtensa,
Endianness::Little,
&[ArchiveKind::Gnu],
),
]
.iter()
.copied()
{
for archive_kind in archive_kinds {
let mut object = write::Object::new(BinaryFormat::Elf, arch, endian);
let section = object.section_id(write::StandardSection::Data);
object.append_section_data(section, &[1; 30], 4);
let symbol = object.section_symbol(section);
object
.add_relocation(
section,
write::Relocation {
offset: 8,
symbol,
addend: 0,
flags: RelocationFlags::Generic {
kind: RelocationKind::Absolute,
encoding: RelocationEncoding::Generic,
size: 32,
},
},
)
.unwrap();
if arch.address_size().unwrap().bytes() >= 8 {
object
.add_relocation(
section,
write::Relocation {
offset: 16,
symbol,
addend: 0,
flags: RelocationFlags::Generic {
kind: RelocationKind::Absolute,
encoding: RelocationEncoding::Generic,
size: 64,
},
},
)
.unwrap();
}
round_trip_and_diff("elf_any", object, *archive_kind, 0);
}
}
}
#[test]
fn macho_x86_64() {
let mut object = write::Object::new(
BinaryFormat::MachO,
Architecture::X86_64,
Endianness::Little,
);
object.add_file_symbol(b"file.c".to_vec());
let text = object.section_id(write::StandardSection::Text);
object.append_section_data(text, &[1; 30], 4);
let func1_offset = object.append_section_data(text, &[1; 30], 4);
assert_eq!(func1_offset, 32);
let func1_symbol = object.add_symbol(write::Symbol {
name: b"func1".to_vec(),
value: func1_offset,
size: 32,
kind: SymbolKind::Text,
scope: SymbolScope::Linkage,
weak: false,
section: write::SymbolSection::Section(text),
flags: SymbolFlags::None,
});
object
.add_relocation(
text,
write::Relocation {
offset: 8,
symbol: func1_symbol,
addend: 0,
flags: RelocationFlags::Generic {
kind: RelocationKind::Absolute,
encoding: RelocationEncoding::Generic,
size: 64,
},
},
)
.unwrap();
object
.add_relocation(
text,
write::Relocation {
offset: 16,
symbol: func1_symbol,
addend: -4,
flags: RelocationFlags::Generic {
kind: RelocationKind::Relative,
encoding: RelocationEncoding::Generic,
size: 32,
},
},
)
.unwrap();
round_trip_and_diff("macho_x86_64", object, ArchiveKind::Darwin, 0);
}
#[test]
fn macho_any() {
// 32-bit object files get additional padding after the round-trip:
// https://github.com/llvm/llvm-project/blob/3d3ef9d073e1e27ea57480b371b7f5a9f5642ed2/llvm/lib/Object/ArchiveWriter.cpp#L560-L565
for (arch, subarch, endian, trim_output_bytes) in [
(Architecture::Aarch64, None, Endianness::Little, 0),
(
Architecture::Aarch64,
Some(SubArchitecture::Arm64E),
Endianness::Little,
0,
),
(Architecture::Aarch64_Ilp32, None, Endianness::Little, 4),
/* TODO:
(Architecture::Arm, None, Endianness::Little),
*/
(Architecture::I386, None, Endianness::Little, 4),
(Architecture::X86_64, None, Endianness::Little, 0),
/* TODO:
(Architecture::PowerPc, None, Endianness::Big),
(Architecture::PowerPc64, None, Endianness::Big),
*/
]
.iter()
.copied()
{
let mut object = write::Object::new(BinaryFormat::MachO, arch, endian);
object.set_sub_architecture(subarch);
let section = object.section_id(write::StandardSection::Data);
object.append_section_data(section, &[1; 30], 4);
let symbol = object.section_symbol(section);
object
.add_relocation(
section,
write::Relocation {
offset: 8,
symbol,
addend: 0,
flags: RelocationFlags::Generic {
kind: RelocationKind::Absolute,
encoding: RelocationEncoding::Generic,
size: 32,
},
},
)
.unwrap();
if arch.address_size().unwrap().bytes() >= 8 {
object
.add_relocation(
section,
write::Relocation {
offset: 16,
symbol,
addend: 0,
flags: RelocationFlags::Generic {
kind: RelocationKind::Absolute,
encoding: RelocationEncoding::Generic,
size: 64,
},
},
)
.unwrap();
}
round_trip_and_diff("macho_any", object, ArchiveKind::Darwin, trim_output_bytes);
}
}
#[test]
fn xcoff_powerpc() {
for arch in [Architecture::PowerPc, Architecture::PowerPc64] {
let mut object = write::Object::new(BinaryFormat::Xcoff, arch, Endianness::Big);
object.add_file_symbol(b"file.c".to_vec());
let text = object.section_id(write::StandardSection::Text);
object.append_section_data(text, &[1; 30], 4);
let func1_offset = object.append_section_data(text, &[1; 30], 4);
assert_eq!(func1_offset, 32);
let func1_symbol = object.add_symbol(write::Symbol {
name: b"func1".to_vec(),
value: func1_offset,
size: 32,
kind: SymbolKind::Text,
scope: SymbolScope::Linkage,
weak: false,
section: write::SymbolSection::Section(text),
flags: SymbolFlags::None,
});
object
.add_relocation(
text,
write::Relocation {
offset: 8,
symbol: func1_symbol,
addend: 0,
flags: RelocationFlags::Generic {
kind: RelocationKind::Absolute,
encoding: RelocationEncoding::Generic,
size: 64,
},
},
)
.unwrap();
round_trip_and_diff("xcoff_powerpc", object, ArchiveKind::Gnu, 0);
}
}