mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
Auto merge of #134604 - RalfJung:miri-sync, r=RalfJung
Miri subtree update r? `@ghost`
This commit is contained in:
commit
54dcff104b
@ -375,16 +375,19 @@ to Miri failing to detect cases of undefined behavior in a program.
|
||||
* `-Zmiri-disable-weak-memory-emulation` disables the emulation of some C++11 weak
|
||||
memory effects.
|
||||
* `-Zmiri-native-lib=<path to a shared object file>` is an experimental flag for providing support
|
||||
for calling native functions from inside the interpreter via FFI. Functions not provided by that
|
||||
file are still executed via the usual Miri shims.
|
||||
**WARNING**: If an invalid/incorrect `.so` file is specified, this can cause Undefined Behavior in Miri itself!
|
||||
And of course, Miri cannot do any checks on the actions taken by the native code.
|
||||
Note that Miri has its own handling of file descriptors, so if you want to replace *some* functions
|
||||
working on file descriptors, you will have to replace *all* of them, or the two kinds of
|
||||
file descriptors will be mixed up.
|
||||
This is **work in progress**; currently, only integer arguments and return values are
|
||||
supported (and no, pointer/integer casts to work around this limitation will not work;
|
||||
they will fail horribly). It also only works on Unix hosts for now.
|
||||
for calling native functions from inside the interpreter via FFI. The flag is supported only on
|
||||
Unix systems. Functions not provided by that file are still executed via the usual Miri shims.
|
||||
**WARNING**: If an invalid/incorrect `.so` file is specified, this can cause Undefined Behavior in
|
||||
Miri itself! And of course, Miri cannot do any checks on the actions taken by the native code.
|
||||
Note that Miri has its own handling of file descriptors, so if you want to replace *some*
|
||||
functions working on file descriptors, you will have to replace *all* of them, or the two kinds of
|
||||
file descriptors will be mixed up. This is **work in progress**; currently, only integer and
|
||||
pointers arguments and return values are supported and memory allocated by the native code cannot
|
||||
be accessed from Rust (only the other way around). Native code must not spawn threads that keep
|
||||
running in the background after the call has returned to Rust and that access Rust-allocated
|
||||
memory. Finally, the flag is **unsound** in the sense that Miri stops tracking details such as
|
||||
initialization and provenance on memory shared with native code, so it is easily possible to write
|
||||
code that has UB which is missed by Miri.
|
||||
* `-Zmiri-measureme=<name>` enables `measureme` profiling for the interpreted program.
|
||||
This can be used to find which parts of your program are executing slowly under Miri.
|
||||
The profile is written out to a file inside a directory called `<name>`, and can be processed
|
||||
|
@ -1,35 +1,35 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.17.0"
|
||||
version = "0.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
|
||||
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
name = "adler2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.65"
|
||||
version = "0.3.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61"
|
||||
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -39,15 +39,6 @@ dependencies = [
|
||||
"backtrace",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9540e661f81799159abee814118cc139a2004b3a3aa3ea37724a1b66530b90e0"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
@ -56,48 +47,106 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.26.1"
|
||||
version = "0.31.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
|
||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.126"
|
||||
version = "0.2.168"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.5.3"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc"
|
||||
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"adler2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.28.4"
|
||||
version = "0.36.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424"
|
||||
checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.21"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
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"
|
||||
|
@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "cargo-miri-test"
|
||||
@ -12,48 +12,54 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.2"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
|
||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.39"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.18"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.10"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.137"
|
||||
version = "1.0.216"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
|
||||
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.137"
|
||||
version = "1.0.216"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
|
||||
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -62,20 +68,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.81"
|
||||
version = "1.0.133"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
|
||||
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.96"
|
||||
version = "2.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -84,6 +91,6 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.0"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "cargo-miri-test"
|
||||
@ -12,48 +12,54 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.2"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
|
||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.39"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.18"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.10"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.137"
|
||||
version = "1.0.216"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
|
||||
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.137"
|
||||
version = "1.0.216"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
|
||||
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -62,20 +68,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.81"
|
||||
version = "1.0.133"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
|
||||
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.96"
|
||||
version = "2.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -84,6 +91,6 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.0"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "unicode"
|
||||
@ -11,6 +11,6 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.3"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
@ -18,7 +18,7 @@ export RUSTFLAGS="-D warnings"
|
||||
export CARGO_INCREMENTAL=0
|
||||
export CARGO_EXTRA_FLAGS="--locked"
|
||||
|
||||
# Determine configuration for installed build (used by test-cargo-miri).
|
||||
# Determine configuration for installed build (used by test-cargo-miri and `./miri bench`).
|
||||
echo "Installing release version of Miri"
|
||||
time ./miri install
|
||||
|
||||
@ -73,7 +73,7 @@ function run_tests {
|
||||
fi
|
||||
if [ -n "${TEST_BENCH-}" ]; then
|
||||
# Check that the benchmarks build and run, but only once.
|
||||
time HYPERFINE="hyperfine -w0 -r1" ./miri bench $TARGET_FLAG
|
||||
time HYPERFINE="hyperfine -w0 -r1 --show-output" ./miri bench $TARGET_FLAG --no-install
|
||||
fi
|
||||
# Smoke-test `./miri run --dep`.
|
||||
./miri run $TARGET_FLAG --dep tests/pass-dep/getrandom.rs
|
||||
|
@ -2,6 +2,55 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.80"
|
||||
@ -20,6 +69,52 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "directories"
|
||||
version = "5.0.1"
|
||||
@ -80,6 +175,12 @@ dependencies = [
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.9"
|
||||
@ -89,6 +190,12 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.11.0"
|
||||
@ -137,6 +244,7 @@ name = "miri-script"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"directories",
|
||||
"dunce",
|
||||
"itertools",
|
||||
@ -278,6 +386,12 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.50"
|
||||
@ -328,6 +442,12 @@ version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.4.0"
|
||||
@ -362,7 +482,7 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -25,3 +25,4 @@ dunce = "1.0.4"
|
||||
directories = "5"
|
||||
serde_json = "1"
|
||||
tempfile = "3.13.0"
|
||||
clap = { version = "4.5.21", features = ["derive"] }
|
||||
|
@ -1,135 +0,0 @@
|
||||
use std::{env, iter};
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
|
||||
pub struct Args {
|
||||
args: iter::Peekable<env::Args>,
|
||||
/// Set to `true` once we saw a `--`.
|
||||
terminated: bool,
|
||||
}
|
||||
|
||||
impl Args {
|
||||
pub fn new() -> Self {
|
||||
let mut args = Args { args: env::args().peekable(), terminated: false };
|
||||
args.args.next().unwrap(); // skip program name
|
||||
args
|
||||
}
|
||||
|
||||
/// Get the next argument without any interpretation.
|
||||
pub fn next_raw(&mut self) -> Option<String> {
|
||||
self.args.next()
|
||||
}
|
||||
|
||||
/// Consume a `-$f` flag if present.
|
||||
pub fn get_short_flag(&mut self, flag: char) -> Result<bool> {
|
||||
if self.terminated {
|
||||
return Ok(false);
|
||||
}
|
||||
if let Some(next) = self.args.peek() {
|
||||
if let Some(next) = next.strip_prefix("-") {
|
||||
if let Some(next) = next.strip_prefix(flag) {
|
||||
if next.is_empty() {
|
||||
self.args.next().unwrap(); // consume this argument
|
||||
return Ok(true);
|
||||
} else {
|
||||
bail!("`-{flag}` followed by value");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// Consume a `--$name` flag if present.
|
||||
pub fn get_long_flag(&mut self, name: &str) -> Result<bool> {
|
||||
if self.terminated {
|
||||
return Ok(false);
|
||||
}
|
||||
if let Some(next) = self.args.peek() {
|
||||
if let Some(next) = next.strip_prefix("--") {
|
||||
if next == name {
|
||||
self.args.next().unwrap(); // consume this argument
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// Consume a `--$name val` or `--$name=val` option if present.
|
||||
pub fn get_long_opt(&mut self, name: &str) -> Result<Option<String>> {
|
||||
assert!(!name.is_empty());
|
||||
if self.terminated {
|
||||
return Ok(None);
|
||||
}
|
||||
let Some(next) = self.args.peek() else { return Ok(None) };
|
||||
let Some(next) = next.strip_prefix("--") else { return Ok(None) };
|
||||
let Some(next) = next.strip_prefix(name) else { return Ok(None) };
|
||||
// Starts with `--flag`.
|
||||
Ok(if let Some(val) = next.strip_prefix("=") {
|
||||
// `--flag=val` form
|
||||
let val = val.into();
|
||||
self.args.next().unwrap(); // consume this argument
|
||||
Some(val)
|
||||
} else if next.is_empty() {
|
||||
// `--flag val` form
|
||||
self.args.next().unwrap(); // consume this argument
|
||||
let Some(val) = self.args.next() else { bail!("`--{name}` not followed by value") };
|
||||
Some(val)
|
||||
} else {
|
||||
// Some unrelated flag, like `--flag-more` or so.
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
/// Consume a `--$name=val` or `--$name` option if present; the latter
|
||||
/// produces a default value. (`--$name val` is *not* accepted for this form
|
||||
/// of argument, it understands `val` already as the next argument!)
|
||||
pub fn get_long_opt_with_default(
|
||||
&mut self,
|
||||
name: &str,
|
||||
default: &str,
|
||||
) -> Result<Option<String>> {
|
||||
assert!(!name.is_empty());
|
||||
if self.terminated {
|
||||
return Ok(None);
|
||||
}
|
||||
let Some(next) = self.args.peek() else { return Ok(None) };
|
||||
let Some(next) = next.strip_prefix("--") else { return Ok(None) };
|
||||
let Some(next) = next.strip_prefix(name) else { return Ok(None) };
|
||||
// Starts with `--flag`.
|
||||
Ok(if let Some(val) = next.strip_prefix("=") {
|
||||
// `--flag=val` form
|
||||
let val = val.into();
|
||||
self.args.next().unwrap(); // consume this argument
|
||||
Some(val)
|
||||
} else if next.is_empty() {
|
||||
// `--flag` form
|
||||
self.args.next().unwrap(); // consume this argument
|
||||
Some(default.into())
|
||||
} else {
|
||||
// Some unrelated flag, like `--flag-more` or so.
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the next free argument or uninterpreted flag, or `None` if there are no more
|
||||
/// arguments left. `--` is returned as well, but it is interpreted in the sense that no more
|
||||
/// flags will be parsed after this.
|
||||
pub fn get_other(&mut self) -> Option<String> {
|
||||
if self.terminated {
|
||||
return self.args.next();
|
||||
}
|
||||
let next = self.args.next()?;
|
||||
if next == "--" {
|
||||
self.terminated = true; // don't parse any more flags
|
||||
// This is where our parser is special, we do yield the `--`.
|
||||
}
|
||||
Some(next)
|
||||
}
|
||||
|
||||
/// Return the rest of the aguments entirely unparsed.
|
||||
pub fn remainder(self) -> Vec<String> {
|
||||
self.args.collect()
|
||||
}
|
||||
}
|
@ -179,7 +179,8 @@ impl Command {
|
||||
Command::Doc { flags } => Self::doc(flags),
|
||||
Command::Fmt { flags } => Self::fmt(flags),
|
||||
Command::Clippy { flags } => Self::clippy(flags),
|
||||
Command::Bench { target, benches } => Self::bench(target, benches),
|
||||
Command::Bench { target, no_install, benches } =>
|
||||
Self::bench(target, no_install, benches),
|
||||
Command::Toolchain { flags } => Self::toolchain(flags),
|
||||
Command::RustcPull { commit } => Self::rustc_pull(commit.clone()),
|
||||
Command::RustcPush { github_user, branch } => Self::rustc_push(github_user, branch),
|
||||
@ -378,7 +379,7 @@ impl Command {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bench(target: Option<String>, benches: Vec<String>) -> Result<()> {
|
||||
fn bench(target: Option<String>, no_install: bool, benches: Vec<String>) -> Result<()> {
|
||||
// The hyperfine to use
|
||||
let hyperfine = env::var("HYPERFINE");
|
||||
let hyperfine = hyperfine.as_deref().unwrap_or("hyperfine -w 1 -m 5 --shell=none");
|
||||
@ -386,8 +387,10 @@ impl Command {
|
||||
let Some((program_name, args)) = hyperfine.split_first() else {
|
||||
bail!("expected HYPERFINE environment variable to be non-empty");
|
||||
};
|
||||
// Make sure we have an up-to-date Miri installed and selected the right toolchain.
|
||||
Self::install(vec![])?;
|
||||
if !no_install {
|
||||
// Make sure we have an up-to-date Miri installed and selected the right toolchain.
|
||||
Self::install(vec![])?;
|
||||
}
|
||||
|
||||
let sh = Shell::new()?;
|
||||
sh.change_dir(miri_dir()?);
|
||||
@ -409,6 +412,7 @@ impl Command {
|
||||
OsString::new()
|
||||
};
|
||||
let target_flag = &target_flag;
|
||||
let toolchain = active_toolchain()?;
|
||||
// Run the requested benchmarks
|
||||
for bench in benches {
|
||||
let current_bench = path!(benches_dir / bench / "Cargo.toml");
|
||||
@ -416,7 +420,7 @@ impl Command {
|
||||
// That seems to make Windows CI happy.
|
||||
cmd!(
|
||||
sh,
|
||||
"{program_name} {args...} 'cargo miri run '{target_flag}' --manifest-path \"'{current_bench}'\"'"
|
||||
"{program_name} {args...} 'cargo +'{toolchain}' miri run '{target_flag}' --manifest-path \"'{current_bench}'\"'"
|
||||
)
|
||||
.run()?;
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
#![allow(clippy::needless_question_mark)]
|
||||
|
||||
mod args;
|
||||
mod commands;
|
||||
mod coverage;
|
||||
mod util;
|
||||
@ -8,250 +7,191 @@ mod util;
|
||||
use std::ops::Range;
|
||||
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
/// Parses a seed range
|
||||
///
|
||||
/// This function is used for the `--many-seeds` flag. It expects the range in the form
|
||||
/// `<from>..<to>`. `<from>` is inclusive, `<to>` is exclusive. `<from>` can be omitted,
|
||||
/// in which case it is assumed to be `0`.
|
||||
fn parse_range(val: &str) -> anyhow::Result<Range<u32>> {
|
||||
let (from, to) = val
|
||||
.split_once("..")
|
||||
.ok_or_else(|| anyhow!("invalid format for `--many-seeds`: expected `from..to`"))?;
|
||||
let from: u32 = if from.is_empty() {
|
||||
0
|
||||
} else {
|
||||
from.parse().context("invalid `from` in `--many-seeds=from..to")?
|
||||
};
|
||||
let to: u32 = to.parse().context("invalid `to` in `--many-seeds=from..to")?;
|
||||
Ok(from..to)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Subcommand)]
|
||||
pub enum Command {
|
||||
/// Installs the miri driver and cargo-miri.
|
||||
/// Installs the miri driver and cargo-miri to the sysroot of the active toolchain.
|
||||
///
|
||||
/// Sets up the rpath such that the installed binary should work in any
|
||||
/// working directory. Note that the binaries are placed in the `miri` toolchain
|
||||
/// sysroot, to prevent conflicts with other toolchains.
|
||||
/// working directory.
|
||||
Install {
|
||||
/// Flags that are passed through to `cargo install`.
|
||||
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||
flags: Vec<String>,
|
||||
},
|
||||
/// Just build miri.
|
||||
/// Build Miri.
|
||||
Build {
|
||||
/// Flags that are passed through to `cargo build`.
|
||||
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||
flags: Vec<String>,
|
||||
},
|
||||
/// Just check miri.
|
||||
/// Check Miri.
|
||||
Check {
|
||||
/// Flags that are passed through to `cargo check`.
|
||||
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||
flags: Vec<String>,
|
||||
},
|
||||
/// Build miri, set up a sysroot and then run the test suite.
|
||||
/// Check Miri with Clippy.
|
||||
Clippy {
|
||||
/// Flags that are passed through to `cargo clippy`.
|
||||
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||
flags: Vec<String>,
|
||||
},
|
||||
/// Run the Miri test suite.
|
||||
Test {
|
||||
/// Update stdout/stderr reference files.
|
||||
#[arg(long)]
|
||||
bless: bool,
|
||||
/// The cross-interpretation target.
|
||||
/// If none then the host is the target.
|
||||
#[arg(long)]
|
||||
target: Option<String>,
|
||||
/// Produce coverage report if set.
|
||||
/// Produce coverage report.
|
||||
#[arg(long)]
|
||||
coverage: bool,
|
||||
/// Flags that are passed through to the test harness.
|
||||
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||
flags: Vec<String>,
|
||||
},
|
||||
/// Build miri, set up a sysroot and then run the driver with the given <flags>.
|
||||
/// (Also respects MIRIFLAGS environment variable.)
|
||||
/// Run the Miri driver.
|
||||
///
|
||||
/// Also respects MIRIFLAGS environment variable.
|
||||
Run {
|
||||
/// Build the program with the dependencies declared in `test_dependencies/Cargo.toml`.
|
||||
#[arg(long)]
|
||||
dep: bool,
|
||||
/// Show build progress.
|
||||
#[arg(long, short)]
|
||||
verbose: bool,
|
||||
/// Run the driver with the seeds in the given range (`..to` or `from..to`, default: `0..64`).
|
||||
#[arg(long, value_parser = parse_range)]
|
||||
many_seeds: Option<Range<u32>>,
|
||||
/// The cross-interpretation target.
|
||||
#[arg(long)]
|
||||
target: Option<String>,
|
||||
/// The Rust edition.
|
||||
#[arg(long)]
|
||||
edition: Option<String>,
|
||||
/// Flags that are passed through to `miri`.
|
||||
///
|
||||
/// The flags set in `MIRIFLAGS` are added in front of these flags.
|
||||
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||
flags: Vec<String>,
|
||||
},
|
||||
/// Build documentation
|
||||
/// Build documentation.
|
||||
Doc {
|
||||
/// Flags that are passed through to `cargo doc`.
|
||||
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||
flags: Vec<String>,
|
||||
},
|
||||
/// Format all sources and tests.
|
||||
Fmt {
|
||||
/// Flags that are passed through to `rustfmt`.
|
||||
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||
flags: Vec<String>,
|
||||
},
|
||||
/// Runs clippy on all sources.
|
||||
Clippy {
|
||||
/// Flags that are passed through to `cargo clippy`.
|
||||
flags: Vec<String>,
|
||||
},
|
||||
/// Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed.
|
||||
/// Runs the benchmarks from bench-cargo-miri in hyperfine.
|
||||
///
|
||||
/// hyperfine needs to be installed.
|
||||
Bench {
|
||||
#[arg(long)]
|
||||
target: Option<String>,
|
||||
/// List of benchmarks to run. By default all benchmarks are run.
|
||||
/// When `true`, skip the `./miri install` step.
|
||||
#[arg(long)]
|
||||
no_install: bool,
|
||||
/// List of benchmarks to run (default: run all benchmarks).
|
||||
benches: Vec<String>,
|
||||
},
|
||||
/// Update and activate the rustup toolchain 'miri' to the commit given in the
|
||||
/// `rust-version` file.
|
||||
/// `rustup-toolchain-install-master` must be installed for this to work. Any extra
|
||||
/// flags are passed to `rustup-toolchain-install-master`.
|
||||
Toolchain { flags: Vec<String> },
|
||||
/// Pull and merge Miri changes from the rustc repo. Defaults to fetching the latest
|
||||
/// rustc commit. The fetched commit is stored in the `rust-version` file, so the
|
||||
/// next `./miri toolchain` will install the rustc that just got pulled.
|
||||
RustcPull { commit: Option<String> },
|
||||
/// Push Miri changes back to the rustc repo. This will pull a copy of the rustc
|
||||
/// history into the Miri repo, unless you set the RUSTC_GIT env var to an existing
|
||||
/// clone of the rustc repo.
|
||||
RustcPush { github_user: String, branch: String },
|
||||
/// Update and activate the rustup toolchain 'miri'.
|
||||
///
|
||||
/// The `rust-version` file is used to determine the commit that will be intsalled.
|
||||
/// `rustup-toolchain-install-master` must be installed for this to work.
|
||||
Toolchain {
|
||||
/// Flags that are passed through to `rustup-toolchain-install-master`.
|
||||
flags: Vec<String>,
|
||||
},
|
||||
/// Pull and merge Miri changes from the rustc repo.
|
||||
///
|
||||
/// The fetched commit is stored in the `rust-version` file, so the next `./miri toolchain` will
|
||||
/// install the rustc that just got pulled.
|
||||
RustcPull {
|
||||
/// The commit to fetch (default: latest rustc commit).
|
||||
commit: Option<String>,
|
||||
},
|
||||
/// Push Miri changes back to the rustc repo.
|
||||
///
|
||||
/// This will pull a copy of the rustc history into the Miri repo, unless you set the RUSTC_GIT
|
||||
/// env var to an existing clone of the rustc repo.
|
||||
RustcPush {
|
||||
/// The Github user that owns the rustc fork to which we should push.
|
||||
github_user: String,
|
||||
/// The branch to push to.
|
||||
#[arg(default_value = "miri-sync")]
|
||||
branch: String,
|
||||
},
|
||||
}
|
||||
|
||||
const HELP: &str = r#" COMMANDS
|
||||
impl Command {
|
||||
fn add_remainder(&mut self, remainder: Vec<String>) -> Result<()> {
|
||||
if remainder.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
./miri build <flags>:
|
||||
Just build miri. <flags> are passed to `cargo build`.
|
||||
match self {
|
||||
Self::Install { flags }
|
||||
| Self::Build { flags }
|
||||
| Self::Check { flags }
|
||||
| Self::Doc { flags }
|
||||
| Self::Fmt { flags }
|
||||
| Self::Toolchain { flags }
|
||||
| Self::Clippy { flags }
|
||||
| Self::Run { flags, .. }
|
||||
| Self::Test { flags, .. } => {
|
||||
flags.extend(remainder);
|
||||
Ok(())
|
||||
}
|
||||
Self::Bench { .. } | Self::RustcPull { .. } | Self::RustcPush { .. } =>
|
||||
bail!("unexpected \"--\" found in arguments"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
./miri check <flags>:
|
||||
Just check miri. <flags> are passed to `cargo check`.
|
||||
|
||||
./miri test [--bless] [--target <target>] <flags>:
|
||||
Build miri, set up a sysroot and then run the test suite.
|
||||
<flags> are passed to the test harness.
|
||||
|
||||
./miri run [--dep] [-v|--verbose] [--many-seeds|--many-seeds=..to|--many-seeds=from..to] <flags>:
|
||||
Build miri, set up a sysroot and then run the driver with the given <flags>.
|
||||
(Also respects MIRIFLAGS environment variable.)
|
||||
If `--many-seeds` is present, Miri is run many times in parallel with different seeds.
|
||||
The range defaults to `0..64`.
|
||||
|
||||
./miri fmt <flags>:
|
||||
Format all sources and tests. <flags> are passed to `rustfmt`.
|
||||
|
||||
./miri clippy <flags>:
|
||||
Runs clippy on all sources. <flags> are passed to `cargo clippy`.
|
||||
|
||||
./miri cargo <flags>:
|
||||
Runs just `cargo <flags>` with the Miri-specific environment variables.
|
||||
Mainly meant to be invoked by rust-analyzer.
|
||||
|
||||
./miri install <flags>:
|
||||
Installs the miri driver and cargo-miri. <flags> are passed to `cargo
|
||||
install`. Sets up the rpath such that the installed binary should work in any
|
||||
working directory. Note that the binaries are placed in the `miri` toolchain
|
||||
sysroot, to prevent conflicts with other toolchains.
|
||||
|
||||
./miri bench [--target <target>] <benches>:
|
||||
Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed.
|
||||
<benches> can explicitly list the benchmarks to run; by default, all of them are run.
|
||||
|
||||
./miri toolchain <flags>:
|
||||
Update and activate the rustup toolchain 'miri' to the commit given in the
|
||||
`rust-version` file.
|
||||
`rustup-toolchain-install-master` must be installed for this to work. Any extra
|
||||
flags are passed to `rustup-toolchain-install-master`.
|
||||
|
||||
./miri rustc-pull <commit>:
|
||||
Pull and merge Miri changes from the rustc repo. Defaults to fetching the latest
|
||||
rustc commit. The fetched commit is stored in the `rust-version` file, so the
|
||||
next `./miri toolchain` will install the rustc that just got pulled.
|
||||
|
||||
./miri rustc-push <github user> [<branch>]:
|
||||
Push Miri changes back to the rustc repo. This will pull a copy of the rustc
|
||||
history into the Miri repo, unless you set the RUSTC_GIT env var to an existing
|
||||
clone of the rustc repo. The branch defaults to `miri-sync`.
|
||||
|
||||
ENVIRONMENT VARIABLES
|
||||
|
||||
MIRI_SYSROOT:
|
||||
If already set, the "sysroot setup" step is skipped.
|
||||
|
||||
CARGO_EXTRA_FLAGS:
|
||||
Pass extra flags to all cargo invocations. (Ignored by `./miri cargo`.)"#;
|
||||
#[derive(Parser)]
|
||||
#[command(after_help = "Environment variables:
|
||||
MIRI_SYSROOT: If already set, the \"sysroot setup\" step is skipped
|
||||
CARGO_EXTRA_FLAGS: Pass extra flags to all cargo invocations")]
|
||||
pub struct Cli {
|
||||
#[command(subcommand)]
|
||||
pub command: Command,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// We are hand-rolling our own argument parser, since `clap` can't express what we need
|
||||
// (https://github.com/clap-rs/clap/issues/5055).
|
||||
let mut args = args::Args::new();
|
||||
let command = match args.next_raw().as_deref() {
|
||||
Some("build") => Command::Build { flags: args.remainder() },
|
||||
Some("check") => Command::Check { flags: args.remainder() },
|
||||
Some("doc") => Command::Doc { flags: args.remainder() },
|
||||
Some("test") => {
|
||||
let mut target = None;
|
||||
let mut bless = false;
|
||||
let mut flags = Vec::new();
|
||||
let mut coverage = false;
|
||||
loop {
|
||||
if args.get_long_flag("bless")? {
|
||||
bless = true;
|
||||
} else if args.get_long_flag("coverage")? {
|
||||
coverage = true;
|
||||
} else if let Some(val) = args.get_long_opt("target")? {
|
||||
target = Some(val);
|
||||
} else if let Some(flag) = args.get_other() {
|
||||
flags.push(flag);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Command::Test { bless, flags, target, coverage }
|
||||
}
|
||||
Some("run") => {
|
||||
let mut dep = false;
|
||||
let mut verbose = false;
|
||||
let mut many_seeds = None;
|
||||
let mut target = None;
|
||||
let mut edition = None;
|
||||
let mut flags = Vec::new();
|
||||
loop {
|
||||
if args.get_long_flag("dep")? {
|
||||
dep = true;
|
||||
} else if args.get_long_flag("verbose")? || args.get_short_flag('v')? {
|
||||
verbose = true;
|
||||
} else if let Some(val) = args.get_long_opt_with_default("many-seeds", "0..64")? {
|
||||
let (from, to) = val.split_once("..").ok_or_else(|| {
|
||||
anyhow!("invalid format for `--many-seeds`: expected `from..to`")
|
||||
})?;
|
||||
let from: u32 = if from.is_empty() {
|
||||
0
|
||||
} else {
|
||||
from.parse().context("invalid `from` in `--many-seeds=from..to")?
|
||||
};
|
||||
let to: u32 = to.parse().context("invalid `to` in `--many-seeds=from..to")?;
|
||||
many_seeds = Some(from..to);
|
||||
} else if let Some(val) = args.get_long_opt("target")? {
|
||||
target = Some(val);
|
||||
} else if let Some(val) = args.get_long_opt("edition")? {
|
||||
edition = Some(val);
|
||||
} else if let Some(flag) = args.get_other() {
|
||||
flags.push(flag);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Command::Run { dep, verbose, many_seeds, target, edition, flags }
|
||||
}
|
||||
Some("fmt") => Command::Fmt { flags: args.remainder() },
|
||||
Some("clippy") => Command::Clippy { flags: args.remainder() },
|
||||
Some("install") => Command::Install { flags: args.remainder() },
|
||||
Some("bench") => {
|
||||
let mut target = None;
|
||||
let mut benches = Vec::new();
|
||||
loop {
|
||||
if let Some(val) = args.get_long_opt("target")? {
|
||||
target = Some(val);
|
||||
} else if let Some(flag) = args.get_other() {
|
||||
benches.push(flag);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Command::Bench { target, benches }
|
||||
}
|
||||
Some("toolchain") => Command::Toolchain { flags: args.remainder() },
|
||||
Some("rustc-pull") => {
|
||||
let commit = args.next_raw();
|
||||
if args.next_raw().is_some() {
|
||||
bail!("Too many arguments for `./miri rustc-pull`");
|
||||
}
|
||||
Command::RustcPull { commit }
|
||||
}
|
||||
Some("rustc-push") => {
|
||||
let github_user = args.next_raw().ok_or_else(|| {
|
||||
anyhow!("Missing first argument for `./miri rustc-push GITHUB_USER [BRANCH]`")
|
||||
})?;
|
||||
let branch = args.next_raw().unwrap_or_else(|| "miri-sync".into());
|
||||
if args.next_raw().is_some() {
|
||||
bail!("Too many arguments for `./miri rustc-push GITHUB_USER BRANCH`");
|
||||
}
|
||||
Command::RustcPush { github_user, branch }
|
||||
}
|
||||
_ => {
|
||||
eprintln!("Unknown or missing command. Usage:\n\n{HELP}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
// Split the arguments into the part before the `--` and the part after.
|
||||
// The `--` itself ends up in the second part.
|
||||
let miri_args: Vec<_> = std::env::args().take_while(|x| *x != "--").collect();
|
||||
let remainder: Vec<_> = std::env::args().skip_while(|x| *x != "--").collect();
|
||||
|
||||
let args = Cli::parse_from(miri_args);
|
||||
let mut command = args.command;
|
||||
command.add_remainder(remainder)?;
|
||||
command.exec()?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
728f2daab42ba8f1b3d5caab62495798d1eabfa1
|
||||
13170cd787cb733ed24842ee825bcbd98dc01476
|
||||
|
@ -237,6 +237,10 @@ impl Permission {
|
||||
pub fn is_active(&self) -> bool {
|
||||
self.inner == Active
|
||||
}
|
||||
/// Check if `self` is the never-allow-writes-again state of a pointer (is `Frozen`).
|
||||
pub fn is_frozen(&self) -> bool {
|
||||
self.inner == Frozen
|
||||
}
|
||||
|
||||
/// Default initial permission of the root of a new tree at inbounds positions.
|
||||
/// Must *only* be used for the root, this is not in general an "initial" permission!
|
||||
|
@ -153,8 +153,31 @@ impl LocationState {
|
||||
) -> ContinueTraversal {
|
||||
if rel_pos.is_foreign() {
|
||||
let happening_now = IdempotentForeignAccess::from_foreign(access_kind);
|
||||
let new_access_noop =
|
||||
let mut new_access_noop =
|
||||
self.idempotent_foreign_access.can_skip_foreign_access(happening_now);
|
||||
if self.permission.is_disabled() {
|
||||
// A foreign access to a `Disabled` tag will have almost no observable effect.
|
||||
// It's a theorem that `Disabled` node have no protected initialized children,
|
||||
// and so this foreign access will never trigger any protector.
|
||||
// (Intuition: You're either protected initialized, and thus can't become Disabled
|
||||
// or you're already Disabled protected, but not initialized, and then can't
|
||||
// become initialized since that requires a child access, which Disabled blocks.)
|
||||
// Further, the children will never be able to read or write again, since they
|
||||
// have a `Disabled` parent. So this only affects diagnostics, such that the
|
||||
// blocking write will still be identified directly, just at a different tag.
|
||||
new_access_noop = true;
|
||||
}
|
||||
if self.permission.is_frozen() && access_kind == AccessKind::Read {
|
||||
// A foreign read to a `Frozen` tag will have almost no observable effect.
|
||||
// It's a theorem that `Frozen` nodes have no active children, so all children
|
||||
// already survive foreign reads. Foreign reads in general have almost no
|
||||
// effect, the only further thing they could do is make protected `Reserved`
|
||||
// nodes become conflicted, i.e. make them reject child writes for the further
|
||||
// duration of their protector. But such a child write is already rejected
|
||||
// because this node is frozen. So this only affects diagnostics, but the
|
||||
// blocking read will still be identified directly, just at a different tag.
|
||||
new_access_noop = true;
|
||||
}
|
||||
if new_access_noop {
|
||||
// Abort traversal if the new access is indeed guaranteed
|
||||
// to be noop.
|
||||
|
@ -159,6 +159,8 @@ pub enum BlockReason {
|
||||
Epoll,
|
||||
/// Blocked on eventfd.
|
||||
Eventfd,
|
||||
/// Blocked on unnamed_socket.
|
||||
UnnamedSocket,
|
||||
}
|
||||
|
||||
/// The state of a thread.
|
||||
|
@ -126,6 +126,7 @@ pub enum NonHaltingDiagnostic {
|
||||
Int2Ptr {
|
||||
details: bool,
|
||||
},
|
||||
NativeCallSharedMem,
|
||||
WeakMemoryOutdatedLoad {
|
||||
ptr: Pointer,
|
||||
},
|
||||
@ -602,6 +603,8 @@ impl<'tcx> MiriMachine<'tcx> {
|
||||
RejectedIsolatedOp(_) =>
|
||||
("operation rejected by isolation".to_string(), DiagLevel::Warning),
|
||||
Int2Ptr { .. } => ("integer-to-pointer cast".to_string(), DiagLevel::Warning),
|
||||
NativeCallSharedMem =>
|
||||
("sharing memory with a native function".to_string(), DiagLevel::Warning),
|
||||
ExternTypeReborrow =>
|
||||
("reborrow of reference to `extern type`".to_string(), DiagLevel::Warning),
|
||||
CreatedPointerTag(..)
|
||||
@ -637,6 +640,7 @@ impl<'tcx> MiriMachine<'tcx> {
|
||||
ProgressReport { .. } =>
|
||||
format!("progress report: current operation being executed is here"),
|
||||
Int2Ptr { .. } => format!("integer-to-pointer cast"),
|
||||
NativeCallSharedMem => format!("sharing memory with a native function called via FFI"),
|
||||
WeakMemoryOutdatedLoad { ptr } =>
|
||||
format!("weak memory emulation: outdated value returned from load at {ptr}"),
|
||||
ExternTypeReborrow =>
|
||||
@ -679,7 +683,29 @@ impl<'tcx> MiriMachine<'tcx> {
|
||||
}
|
||||
v
|
||||
}
|
||||
NativeCallSharedMem => {
|
||||
vec![
|
||||
note!(
|
||||
"when memory is shared with a native function call, Miri stops tracking initialization and provenance for that memory"
|
||||
),
|
||||
note!(
|
||||
"in particular, Miri assumes that the native call initializes all memory it has access to"
|
||||
),
|
||||
note!(
|
||||
"Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory"
|
||||
),
|
||||
note!(
|
||||
"what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free"
|
||||
),
|
||||
]
|
||||
}
|
||||
ExternTypeReborrow => {
|
||||
assert!(self.borrow_tracker.as_ref().is_some_and(|b| {
|
||||
matches!(
|
||||
b.borrow().borrow_tracker_method(),
|
||||
BorrowTrackerMethod::StackedBorrows
|
||||
)
|
||||
}));
|
||||
vec![
|
||||
note!(
|
||||
"`extern type` are not compatible with the Stacked Borrows aliasing model implemented by Miri; Miri may miss bugs in this code"
|
||||
|
@ -310,18 +310,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.layout_of(array_ty).unwrap()
|
||||
}
|
||||
|
||||
/// Project to the given *named* field (which must be a struct or union type).
|
||||
fn try_project_field_named<P: Projectable<'tcx, Provenance>>(
|
||||
&self,
|
||||
base: &P,
|
||||
name: &str,
|
||||
) -> InterpResult<'tcx, Option<P>> {
|
||||
let this = self.eval_context_ref();
|
||||
let adt = base.layout().ty.ty_adt_def().unwrap();
|
||||
for (idx, field) in adt.non_enum_variant().fields.iter().enumerate() {
|
||||
if field.name.as_str() == name {
|
||||
return interp_ok(Some(this.project_field(base, idx)?));
|
||||
}
|
||||
}
|
||||
interp_ok(None)
|
||||
}
|
||||
|
||||
/// Project to the given *named* field (which must be a struct or union type).
|
||||
fn project_field_named<P: Projectable<'tcx, Provenance>>(
|
||||
&self,
|
||||
base: &P,
|
||||
name: &str,
|
||||
) -> InterpResult<'tcx, P> {
|
||||
let this = self.eval_context_ref();
|
||||
let adt = base.layout().ty.ty_adt_def().unwrap();
|
||||
for (idx, field) in adt.non_enum_variant().fields.iter().enumerate() {
|
||||
if field.name.as_str() == name {
|
||||
return this.project_field(base, idx);
|
||||
}
|
||||
if let Some(field) = self.try_project_field_named(base, name)? {
|
||||
return interp_ok(field);
|
||||
}
|
||||
bug!("No field named {} in type {}", name, base.layout().ty);
|
||||
}
|
||||
@ -332,13 +344,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
base: &P,
|
||||
name: &str,
|
||||
) -> bool {
|
||||
let adt = base.layout().ty.ty_adt_def().unwrap();
|
||||
for field in adt.non_enum_variant().fields.iter() {
|
||||
if field.name.as_str() == name {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
self.try_project_field_named(base, name).unwrap().is_some()
|
||||
}
|
||||
|
||||
/// Write an int of the appropriate size to `dest`. The target type may be signed or unsigned,
|
||||
@ -921,7 +927,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
if fn_abi.conv != exp_abi {
|
||||
throw_ub_format!(
|
||||
"calling a function with ABI {:?} using caller ABI {:?}",
|
||||
exp_abi, fn_abi.conv);
|
||||
exp_abi,
|
||||
fn_abi.conv
|
||||
);
|
||||
}
|
||||
interp_ok(())
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ use rand::{Rng, SeedableRng};
|
||||
use rustc_abi::{Align, ExternAbi, Size};
|
||||
use rustc_attr_parsing::InlineAttr;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_target::callconv::FnAbi;
|
||||
#[allow(unused)]
|
||||
use rustc_data_structures::static_assert_size;
|
||||
use rustc_middle::mir;
|
||||
@ -25,6 +24,7 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
|
||||
use rustc_session::config::InliningThreshold;
|
||||
use rustc_span::def_id::{CrateNum, DefId};
|
||||
use rustc_span::{Span, SpanData, Symbol};
|
||||
use rustc_target::callconv::FnAbi;
|
||||
|
||||
use crate::concurrency::cpu_affinity::{self, CpuAffinityMask};
|
||||
use crate::concurrency::data_race::{self, NaReadType, NaWriteType};
|
||||
|
@ -9,12 +9,11 @@ use rustc_ast::expand::allocator::alloc_error_handler_name;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::CrateNum;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::{mir, ty};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::{mir, ty};
|
||||
use rustc_span::Symbol;
|
||||
use rustc_target::callconv::{Conv, FnAbi};
|
||||
|
||||
|
||||
use self::helpers::{ToHost, ToSoft};
|
||||
use super::alloc::EvalContextExt as _;
|
||||
use super::backtrace::EvalContextExt as _;
|
||||
@ -279,7 +278,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
match link_name.as_str() {
|
||||
// Miri-specific extern functions
|
||||
"miri_start_unwind" => {
|
||||
let [payload] = this.check_shim(abi, Conv::Rust, link_name, args)?;
|
||||
let [payload] = this.check_shim(abi, Conv::Rust, link_name, args)?;
|
||||
this.handle_miri_start_unwind(payload)?;
|
||||
return interp_ok(EmulateItemResult::NeedsUnwind);
|
||||
}
|
||||
@ -288,7 +287,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.run_provenance_gc();
|
||||
}
|
||||
"miri_get_alloc_id" => {
|
||||
let [ptr] = this.check_shim(abi, Conv::Rust, link_name, args)?;
|
||||
let [ptr] = this.check_shim(abi, Conv::Rust, link_name, args)?;
|
||||
let ptr = this.read_pointer(ptr)?;
|
||||
let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err_kind(|_e| {
|
||||
err_machine_stop!(TerminationInfo::Abort(format!(
|
||||
@ -298,7 +297,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.write_scalar(Scalar::from_u64(alloc_id.0.get()), dest)?;
|
||||
}
|
||||
"miri_print_borrow_state" => {
|
||||
let [id, show_unnamed] = this.check_shim(abi, Conv::Rust, link_name, args)?;
|
||||
let [id, show_unnamed] = this.check_shim(abi, Conv::Rust, link_name, args)?;
|
||||
let id = this.read_scalar(id)?.to_u64()?;
|
||||
let show_unnamed = this.read_scalar(show_unnamed)?.to_bool()?;
|
||||
if let Some(id) = std::num::NonZero::new(id).map(AllocId)
|
||||
@ -312,8 +311,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
"miri_pointer_name" => {
|
||||
// This associates a name to a tag. Very useful for debugging, and also makes
|
||||
// tests more strict.
|
||||
let [ptr, nth_parent, name] =
|
||||
this.check_shim(abi, Conv::Rust, link_name, args)?;
|
||||
let [ptr, nth_parent, name] = this.check_shim(abi, Conv::Rust, link_name, args)?;
|
||||
let ptr = this.read_pointer(ptr)?;
|
||||
let nth_parent = this.read_scalar(nth_parent)?.to_u8()?;
|
||||
let name = this.read_immediate(name)?;
|
||||
@ -337,8 +335,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.machine.static_roots.push(alloc_id);
|
||||
}
|
||||
"miri_host_to_target_path" => {
|
||||
let [ptr, out, out_size] =
|
||||
this.check_shim(abi, Conv::Rust, link_name, args)?;
|
||||
let [ptr, out, out_size] = this.check_shim(abi, Conv::Rust, link_name, args)?;
|
||||
let ptr = this.read_pointer(ptr)?;
|
||||
let out = this.read_pointer(out)?;
|
||||
let out_size = this.read_scalar(out_size)?.to_target_usize(this)?;
|
||||
@ -429,13 +426,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
// Aborting the process.
|
||||
"exit" => {
|
||||
let [code] =
|
||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
||||
let [code] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let code = this.read_scalar(code)?.to_i32()?;
|
||||
throw_machine_stop!(TerminationInfo::Exit { code: code.into(), leak_check: false });
|
||||
}
|
||||
"abort" => {
|
||||
let [] = this.check_shim(abi, Conv::C , link_name, args)?;
|
||||
let [] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
throw_machine_stop!(TerminationInfo::Abort(
|
||||
"the program aborted execution".to_owned()
|
||||
))
|
||||
@ -443,8 +439,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
// Standard C allocation
|
||||
"malloc" => {
|
||||
let [size] =
|
||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
||||
let [size] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let size = this.read_target_usize(size)?;
|
||||
if size <= this.max_size_of_val().bytes() {
|
||||
let res = this.malloc(size, /*zero_init:*/ false)?;
|
||||
@ -458,8 +453,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
}
|
||||
"calloc" => {
|
||||
let [items, elem_size] =
|
||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
||||
let [items, elem_size] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let items = this.read_target_usize(items)?;
|
||||
let elem_size = this.read_target_usize(elem_size)?;
|
||||
if let Some(size) = this.compute_size_in_bytes(Size::from_bytes(elem_size), items) {
|
||||
@ -474,14 +468,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
}
|
||||
"free" => {
|
||||
let [ptr] =
|
||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
||||
let [ptr] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let ptr = this.read_pointer(ptr)?;
|
||||
this.free(ptr)?;
|
||||
}
|
||||
"realloc" => {
|
||||
let [old_ptr, new_size] =
|
||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
||||
let [old_ptr, new_size] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let old_ptr = this.read_pointer(old_ptr)?;
|
||||
let new_size = this.read_target_usize(new_size)?;
|
||||
if new_size <= this.max_size_of_val().bytes() {
|
||||
@ -619,8 +611,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
// C memory handling functions
|
||||
"memcmp" => {
|
||||
let [left, right, n] =
|
||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
||||
let [left, right, n] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let left = this.read_pointer(left)?;
|
||||
let right = this.read_pointer(right)?;
|
||||
let n = Size::from_bytes(this.read_target_usize(n)?);
|
||||
@ -644,8 +635,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
}
|
||||
"memrchr" => {
|
||||
let [ptr, val, num] =
|
||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
||||
let [ptr, val, num] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let ptr = this.read_pointer(ptr)?;
|
||||
let val = this.read_scalar(val)?.to_i32()?;
|
||||
let num = this.read_target_usize(num)?;
|
||||
@ -671,8 +661,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
}
|
||||
"memchr" => {
|
||||
let [ptr, val, num] =
|
||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
||||
let [ptr, val, num] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let ptr = this.read_pointer(ptr)?;
|
||||
let val = this.read_scalar(val)?.to_i32()?;
|
||||
let num = this.read_target_usize(num)?;
|
||||
@ -695,8 +684,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
}
|
||||
"strlen" => {
|
||||
let [ptr] =
|
||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
||||
let [ptr] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let ptr = this.read_pointer(ptr)?;
|
||||
// This reads at least 1 byte, so we are already enforcing that this is a valid pointer.
|
||||
let n = this.read_c_str(ptr)?.len();
|
||||
@ -706,8 +694,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
)?;
|
||||
}
|
||||
"wcslen" => {
|
||||
let [ptr] =
|
||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
||||
let [ptr] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let ptr = this.read_pointer(ptr)?;
|
||||
// This reads at least 1 byte, so we are already enforcing that this is a valid pointer.
|
||||
let n = this.read_wchar_t_str(ptr)?.len();
|
||||
@ -717,8 +704,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
)?;
|
||||
}
|
||||
"memcpy" => {
|
||||
let [ptr_dest, ptr_src, n] =
|
||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
||||
let [ptr_dest, ptr_src, n] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let ptr_dest = this.read_pointer(ptr_dest)?;
|
||||
let ptr_src = this.read_pointer(ptr_src)?;
|
||||
let n = this.read_target_usize(n)?;
|
||||
@ -732,8 +718,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.write_pointer(ptr_dest, dest)?;
|
||||
}
|
||||
"strcpy" => {
|
||||
let [ptr_dest, ptr_src] =
|
||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
||||
let [ptr_dest, ptr_src] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let ptr_dest = this.read_pointer(ptr_dest)?;
|
||||
let ptr_src = this.read_pointer(ptr_src)?;
|
||||
|
||||
@ -878,8 +863,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"lgammaf_r" => {
|
||||
let [x, signp] =
|
||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
||||
let [x, signp] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let x = this.read_scalar(x)?.to_f32()?;
|
||||
let signp = this.deref_pointer(signp)?;
|
||||
|
||||
@ -890,8 +874,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"lgamma_r" => {
|
||||
let [x, signp] =
|
||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
||||
let [x, signp] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let x = this.read_scalar(x)?.to_f64()?;
|
||||
let signp = this.deref_pointer(signp)?;
|
||||
|
||||
@ -904,8 +887,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
// LLVM intrinsics
|
||||
"llvm.prefetch" => {
|
||||
let [p, rw, loc, ty] =
|
||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
||||
let [p, rw, loc, ty] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let _ = this.read_pointer(p)?;
|
||||
let rw = this.read_scalar(rw)?.to_i32()?;
|
||||
@ -932,7 +914,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Used to implement the x86 `_mm{,256,512}_popcnt_epi{8,16,32,64}` and wasm
|
||||
// `{i,u}8x16_popcnt` functions.
|
||||
name if name.starts_with("llvm.ctpop.v") => {
|
||||
let [op] = this.check_shim(abi, Conv::C , link_name, args)?;
|
||||
let [op] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (op, op_len) = this.project_to_simd(op)?;
|
||||
let (dest, dest_len) = this.project_to_simd(dest)?;
|
||||
|
@ -1,4 +1,5 @@
|
||||
//! Implements calling functions from a native library.
|
||||
use std::cell::RefCell;
|
||||
use std::ops::Deref;
|
||||
|
||||
use libffi::high::call as ffi;
|
||||
@ -172,6 +173,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Wildcard pointer, whatever it points to must be already exposed.
|
||||
continue;
|
||||
};
|
||||
// The first time this happens at a particular location, print a warning.
|
||||
thread_local! {
|
||||
static HAVE_WARNED: RefCell<bool> = const { RefCell::new(false) };
|
||||
}
|
||||
HAVE_WARNED.with_borrow_mut(|have_warned| {
|
||||
if !*have_warned {
|
||||
// Newly inserted, so first time we see this span.
|
||||
this.emit_diagnostic(NonHaltingDiagnostic::NativeCallSharedMem);
|
||||
*have_warned = true;
|
||||
}
|
||||
});
|
||||
|
||||
this.prepare_for_native_call(alloc_id, prov)?;
|
||||
}
|
||||
}
|
||||
|
@ -180,6 +180,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
if !matches!(&*this.tcx.sess.target.os, "solaris" | "illumos") {
|
||||
// tm_zone represents the timezone value in the form of: +0730, +08, -0730 or -08.
|
||||
// This may not be consistent with libc::localtime_r's result.
|
||||
|
||||
let offset_in_seconds = dt.offset().fix().local_minus_utc();
|
||||
let tm_gmtoff = offset_in_seconds;
|
||||
let mut tm_zone = String::new();
|
||||
@ -195,11 +196,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
write!(tm_zone, "{:02}", offset_min).unwrap();
|
||||
}
|
||||
|
||||
// FIXME: String de-duplication is needed so that we only allocate this string only once
|
||||
// even when there are multiple calls to this function.
|
||||
let tm_zone_ptr = this
|
||||
.alloc_os_str_as_c_str(&OsString::from(tm_zone), MiriMemoryKind::Machine.into())?;
|
||||
// Add null terminator for C string compatibility.
|
||||
tm_zone.push('\0');
|
||||
|
||||
// Deduplicate and allocate the string.
|
||||
let tm_zone_ptr = this.allocate_bytes_dedup(tm_zone.as_bytes())?;
|
||||
|
||||
// Write the timezone pointer and offset into the result structure.
|
||||
this.write_pointer(tm_zone_ptr, &this.project_field_named(&result, "tm_zone")?)?;
|
||||
this.write_int_fields_named(&[("tm_gmtoff", tm_gmtoff.into())], &result)?;
|
||||
}
|
||||
|
@ -2,8 +2,6 @@ use rustc_middle::ty::Ty;
|
||||
use rustc_span::Symbol;
|
||||
use rustc_target::callconv::{Conv, FnAbi};
|
||||
|
||||
|
||||
|
||||
use crate::shims::unix::android::thread::prctl;
|
||||
use crate::shims::unix::linux_like::epoll::EvalContextExt as _;
|
||||
use crate::shims::unix::linux_like::eventfd::EvalContextExt as _;
|
||||
@ -27,14 +25,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
match link_name.as_str() {
|
||||
// epoll, eventfd
|
||||
"epoll_create1" => {
|
||||
let [flag] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [flag] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.epoll_create1(flag)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"epoll_ctl" => {
|
||||
let [epfd, op, fd, event] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [epfd, op, fd, event] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.epoll_ctl(epfd, op, fd, event)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
@ -44,8 +40,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.epoll_wait(epfd, events, maxevents, timeout, dest)?;
|
||||
}
|
||||
"eventfd" => {
|
||||
let [val, flag] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [val, flag] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.eventfd(val, flag)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
|
@ -2,8 +2,8 @@ use std::ffi::OsStr;
|
||||
use std::str;
|
||||
|
||||
use rustc_abi::Size;
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_span::Symbol;
|
||||
use rustc_target::callconv::{Conv, FnAbi};
|
||||
|
||||
|
@ -22,8 +22,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
match link_name.as_str() {
|
||||
// Threading
|
||||
"pthread_set_name_np" => {
|
||||
let [thread, name] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [thread, name] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let max_len = usize::MAX; // FreeBSD does not seem to have a limit.
|
||||
// FreeBSD's pthread_set_name_np does not return anything.
|
||||
this.pthread_setname_np(
|
||||
@ -34,8 +33,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
)?;
|
||||
}
|
||||
"pthread_get_name_np" => {
|
||||
let [thread, name, len] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [thread, name, len] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
// FreeBSD's pthread_get_name_np does not return anything
|
||||
// and uses strlcpy, which truncates the resulting value,
|
||||
// but always adds a null terminator (except for zero-sized buffers).
|
||||
@ -52,26 +50,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// For those, we both intercept `func` and `call@FBSD_1.0` symbols cases
|
||||
// since freebsd 12 the former form can be expected.
|
||||
"stat" | "stat@FBSD_1.0" => {
|
||||
let [path, buf] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.macos_fbsd_solaris_stat(path, buf)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"lstat" | "lstat@FBSD_1.0" => {
|
||||
let [path, buf] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.macos_fbsd_solaris_lstat(path, buf)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"fstat" | "fstat@FBSD_1.0" => {
|
||||
let [fd, buf] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [fd, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.macos_fbsd_solaris_fstat(fd, buf)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"readdir_r" | "readdir_r@FBSD_1.0" => {
|
||||
let [dirp, entry, result] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [dirp, entry, result] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.macos_fbsd_readdir_r(dirp, entry, result)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
@ -86,8 +80,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
|
||||
// These shims are enabled only when the caller is in the standard library.
|
||||
"pthread_attr_get_np" if this.frame_in_std() => {
|
||||
let [_thread, _attr] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [_thread, _attr] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
|
||||
|
@ -1048,10 +1048,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn linux_readdir64(&mut self, dirp_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
|
||||
fn linux_solarish_readdir64(
|
||||
&mut self,
|
||||
dirent_type: &str,
|
||||
dirp_op: &OpTy<'tcx>,
|
||||
) -> InterpResult<'tcx, Scalar> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
this.assert_target_os("linux", "readdir64");
|
||||
if !matches!(&*this.tcx.sess.target.os, "linux" | "solaris" | "illumos") {
|
||||
panic!("`linux_solaris_readdir64` should not be called on {}", this.tcx.sess.target.os);
|
||||
}
|
||||
|
||||
let dirp = this.read_target_usize(dirp_op)?;
|
||||
|
||||
@ -1070,9 +1076,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
Some(Ok(dir_entry)) => {
|
||||
// Write the directory entry into a newly allocated buffer.
|
||||
// The name is written with write_bytes, while the rest of the
|
||||
// dirent64 struct is written using write_int_fields.
|
||||
// dirent64 (or dirent) struct is written using write_int_fields.
|
||||
|
||||
// For reference:
|
||||
// On Linux:
|
||||
// pub struct dirent64 {
|
||||
// pub d_ino: ino64_t,
|
||||
// pub d_off: off64_t,
|
||||
@ -1080,19 +1087,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// pub d_type: c_uchar,
|
||||
// pub d_name: [c_char; 256],
|
||||
// }
|
||||
//
|
||||
// On Solaris:
|
||||
// pub struct dirent {
|
||||
// pub d_ino: ino64_t,
|
||||
// pub d_off: off64_t,
|
||||
// pub d_reclen: c_ushort,
|
||||
// pub d_name: [c_char; 3],
|
||||
// }
|
||||
|
||||
let mut name = dir_entry.file_name(); // not a Path as there are no separators!
|
||||
name.push("\0"); // Add a NUL terminator
|
||||
let name_bytes = name.as_encoded_bytes();
|
||||
let name_len = u64::try_from(name_bytes.len()).unwrap();
|
||||
|
||||
let dirent64_layout = this.libc_ty_layout("dirent64");
|
||||
let d_name_offset = dirent64_layout.fields.offset(4 /* d_name */).bytes();
|
||||
let dirent_layout = this.libc_ty_layout(dirent_type);
|
||||
let fields = &dirent_layout.fields;
|
||||
let last_field = fields.count().strict_sub(1);
|
||||
let d_name_offset = fields.offset(last_field).bytes();
|
||||
let size = d_name_offset.strict_add(name_len);
|
||||
|
||||
let entry = this.allocate_ptr(
|
||||
Size::from_bytes(size),
|
||||
dirent64_layout.align.abi,
|
||||
dirent_layout.align.abi,
|
||||
MiriMemoryKind::Runtime.into(),
|
||||
)?;
|
||||
let entry: Pointer = entry.into();
|
||||
@ -1105,17 +1122,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
let ino = 0u64;
|
||||
|
||||
let file_type = this.file_type_to_d_type(dir_entry.file_type())?;
|
||||
|
||||
this.write_int_fields_named(
|
||||
&[
|
||||
("d_ino", ino.into()),
|
||||
("d_off", 0),
|
||||
("d_reclen", size.into()),
|
||||
("d_type", file_type.into()),
|
||||
],
|
||||
&this.ptr_to_mplace(entry, dirent64_layout),
|
||||
&[("d_ino", ino.into()), ("d_off", 0), ("d_reclen", size.into())],
|
||||
&this.ptr_to_mplace(entry, dirent_layout),
|
||||
)?;
|
||||
|
||||
if let Some(d_type) = this
|
||||
.try_project_field_named(&this.ptr_to_mplace(entry, dirent_layout), "d_type")?
|
||||
{
|
||||
this.write_int(file_type, &d_type)?;
|
||||
}
|
||||
|
||||
let name_ptr = entry.wrapping_offset(Size::from_bytes(d_name_offset), this);
|
||||
this.write_bytes_ptr(name_ptr, name_bytes.iter().copied())?;
|
||||
|
||||
|
@ -36,14 +36,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
match link_name.as_str() {
|
||||
// File related shims
|
||||
"readdir64" => {
|
||||
let [dirp] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.linux_readdir64(dirp)?;
|
||||
let [dirp] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.linux_solarish_readdir64("dirent64", dirp)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"sync_file_range" => {
|
||||
let [fd, offset, nbytes, flags] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [fd, offset, nbytes, flags] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.sync_file_range(fd, offset, nbytes, flags)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
@ -56,14 +54,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
// epoll, eventfd
|
||||
"epoll_create1" => {
|
||||
let [flag] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [flag] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.epoll_create1(flag)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"epoll_ctl" => {
|
||||
let [epfd, op, fd, event] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [epfd, op, fd, event] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.epoll_ctl(epfd, op, fd, event)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
@ -73,16 +69,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.epoll_wait(epfd, events, maxevents, timeout, dest)?;
|
||||
}
|
||||
"eventfd" => {
|
||||
let [val, flag] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [val, flag] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.eventfd(val, flag)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
|
||||
// Threading
|
||||
"pthread_setname_np" => {
|
||||
let [thread, name] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [thread, name] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let res = match this.pthread_setname_np(
|
||||
this.read_scalar(thread)?,
|
||||
this.read_scalar(name)?,
|
||||
@ -97,8 +91,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"pthread_getname_np" => {
|
||||
let [thread, name, len] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [thread, name, len] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
// The function's behavior isn't portable between platforms.
|
||||
// In case of glibc, the length of the output buffer must
|
||||
// be not shorter than TASK_COMM_LEN.
|
||||
@ -146,8 +139,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.write_scalar(ptr, dest)?;
|
||||
}
|
||||
"__xpg_strerror_r" => {
|
||||
let [errnum, buf, buflen] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [errnum, buf, buflen] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.strerror_r(errnum, buf, buflen)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
@ -170,8 +162,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
|
||||
// These shims are enabled only when the caller is in the standard library.
|
||||
"pthread_getattr_np" if this.frame_in_std() => {
|
||||
let [_thread, _attr] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [_thread, _attr] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@ use rustc_middle::ty::Ty;
|
||||
use rustc_span::Symbol;
|
||||
use rustc_target::callconv::{Conv, FnAbi};
|
||||
|
||||
|
||||
use crate::helpers::check_min_arg_count;
|
||||
use crate::shims::unix::linux_like::eventfd::EvalContextExt as _;
|
||||
use crate::shims::unix::linux_like::sync::futex;
|
||||
|
@ -33,44 +33,37 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
// File related shims
|
||||
"close$NOCANCEL" => {
|
||||
let [result] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [result] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.close(result)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"stat" | "stat64" | "stat$INODE64" => {
|
||||
let [path, buf] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.macos_fbsd_solaris_stat(path, buf)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"lstat" | "lstat64" | "lstat$INODE64" => {
|
||||
let [path, buf] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.macos_fbsd_solaris_lstat(path, buf)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"fstat" | "fstat64" | "fstat$INODE64" => {
|
||||
let [fd, buf] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [fd, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.macos_fbsd_solaris_fstat(fd, buf)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"opendir$INODE64" => {
|
||||
let [name] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [name] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.opendir(name)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"readdir_r" | "readdir_r$INODE64" => {
|
||||
let [dirp, entry, result] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [dirp, entry, result] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.macos_fbsd_readdir_r(dirp, entry, result)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"realpath$DARWIN_EXTSN" => {
|
||||
let [path, resolved_path] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [path, resolved_path] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.realpath(path, resolved_path)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
@ -84,8 +77,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
// Random data generation
|
||||
"CCRandomGenerateBytes" => {
|
||||
let [bytes, count] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [bytes, count] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let bytes = this.read_pointer(bytes)?;
|
||||
let count = this.read_target_usize(count)?;
|
||||
let success = this.eval_libc_i32("kCCSuccess");
|
||||
@ -101,8 +93,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
|
||||
"mach_timebase_info" => {
|
||||
let [info] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [info] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.mach_timebase_info(info)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
@ -117,8 +108,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.write_pointer(this.machine.argv.expect("machine must be initialized"), dest)?;
|
||||
}
|
||||
"_NSGetExecutablePath" => {
|
||||
let [buf, bufsize] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [buf, bufsize] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
this.check_no_isolation("`_NSGetExecutablePath`")?;
|
||||
|
||||
let buf_ptr = this.read_pointer(buf)?;
|
||||
@ -143,8 +133,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
// Thread-local storage
|
||||
"_tlv_atexit" => {
|
||||
let [dtor, data] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [dtor, data] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let dtor = this.read_pointer(dtor)?;
|
||||
let dtor = this.get_ptr_fn(dtor)?.as_instance()?;
|
||||
let data = this.read_scalar(data)?;
|
||||
@ -154,15 +143,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
// Querying system information
|
||||
"pthread_get_stackaddr_np" => {
|
||||
let [thread] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [thread] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
this.read_target_usize(thread)?;
|
||||
let stack_addr = Scalar::from_uint(this.machine.stack_addr, this.pointer_size());
|
||||
this.write_scalar(stack_addr, dest)?;
|
||||
}
|
||||
"pthread_get_stacksize_np" => {
|
||||
let [thread] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [thread] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
this.read_target_usize(thread)?;
|
||||
let stack_size = Scalar::from_uint(this.machine.stack_size, this.pointer_size());
|
||||
this.write_scalar(stack_size, dest)?;
|
||||
@ -170,8 +157,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
// Threading
|
||||
"pthread_setname_np" => {
|
||||
let [name] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [name] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
// The real implementation has logic in two places:
|
||||
// * in userland at https://github.com/apple-oss-distributions/libpthread/blob/c032e0b076700a0a47db75528a282b8d3a06531a/src/pthread.c#L1178-L1200,
|
||||
@ -198,8 +184,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"pthread_getname_np" => {
|
||||
let [thread, name, len] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [thread, name, len] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
// The function's behavior isn't portable between platforms.
|
||||
// In case of macOS, a truncated name (due to a too small buffer)
|
||||
@ -224,28 +209,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
|
||||
"os_unfair_lock_lock" => {
|
||||
let [lock_op] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [lock_op] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
this.os_unfair_lock_lock(lock_op)?;
|
||||
}
|
||||
"os_unfair_lock_trylock" => {
|
||||
let [lock_op] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [lock_op] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
this.os_unfair_lock_trylock(lock_op, dest)?;
|
||||
}
|
||||
"os_unfair_lock_unlock" => {
|
||||
let [lock_op] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [lock_op] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
this.os_unfair_lock_unlock(lock_op)?;
|
||||
}
|
||||
"os_unfair_lock_assert_owner" => {
|
||||
let [lock_op] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [lock_op] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
this.os_unfair_lock_assert_owner(lock_op)?;
|
||||
}
|
||||
"os_unfair_lock_assert_not_owner" => {
|
||||
let [lock_op] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [lock_op] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
this.os_unfair_lock_assert_not_owner(lock_op)?;
|
||||
}
|
||||
|
||||
|
@ -23,8 +23,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
match link_name.as_str() {
|
||||
// Threading
|
||||
"pthread_setname_np" => {
|
||||
let [thread, name] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [thread, name] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
// THREAD_NAME_MAX allows a thread name of 31+1 length
|
||||
// https://github.com/illumos/illumos-gate/blob/7671517e13b8123748eda4ef1ee165c6d9dba7fe/usr/src/uts/common/sys/thread.h#L613
|
||||
let max_len = 32;
|
||||
@ -42,8 +41,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"pthread_getname_np" => {
|
||||
let [thread, name, len] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [thread, name, len] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
// See https://illumos.org/man/3C/pthread_getname_np for the error codes.
|
||||
let res = match this.pthread_getname_np(
|
||||
this.read_scalar(thread)?,
|
||||
@ -60,23 +58,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
// File related shims
|
||||
"stat" | "stat64" => {
|
||||
let [path, buf] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.macos_fbsd_solaris_stat(path, buf)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"lstat" | "lstat64" => {
|
||||
let [path, buf] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.macos_fbsd_solaris_lstat(path, buf)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"fstat" | "fstat64" => {
|
||||
let [fd, buf] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [fd, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.macos_fbsd_solaris_fstat(fd, buf)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"readdir" => {
|
||||
let [dirp] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.linux_solarish_readdir64("dirent", dirp)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
|
||||
// Miscellaneous
|
||||
"___errno" => {
|
||||
@ -86,8 +86,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
|
||||
"stack_getbounds" => {
|
||||
let [stack] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [stack] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let stack = this.deref_pointer_as(stack, this.libc_ty_layout("stack_t"))?;
|
||||
|
||||
this.write_int_fields_named(
|
||||
@ -105,8 +104,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
|
||||
"pset_info" => {
|
||||
let [pset, tpe, cpus, list] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [pset, tpe, cpus, list] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
// We do not need to handle the current process cpu mask, available_parallelism
|
||||
// implementation pass null anyway. We only care for the number of
|
||||
// cpus.
|
||||
@ -135,8 +133,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
|
||||
"__sysconf_xpg7" => {
|
||||
let [val] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [val] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.sysconf(val)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
|
@ -36,6 +36,12 @@ struct AnonSocket {
|
||||
/// This flag is set to `true` if the peer's `readbuf` is non-empty at the time
|
||||
/// of closure.
|
||||
peer_lost_data: Cell<bool>,
|
||||
/// A list of thread ids blocked because the buffer was empty.
|
||||
/// Once another thread writes some bytes, these threads will be unblocked.
|
||||
blocked_read_tid: RefCell<Vec<ThreadId>>,
|
||||
/// A list of thread ids blocked because the buffer was full.
|
||||
/// Once another thread reads some bytes, these threads will be unblocked.
|
||||
blocked_write_tid: RefCell<Vec<ThreadId>>,
|
||||
is_nonblock: bool,
|
||||
}
|
||||
|
||||
@ -83,7 +89,7 @@ impl FileDescription for AnonSocket {
|
||||
|
||||
fn read<'tcx>(
|
||||
&self,
|
||||
_self_ref: &FileDescriptionRef,
|
||||
self_ref: &FileDescriptionRef,
|
||||
_communicate_allowed: bool,
|
||||
ptr: Pointer,
|
||||
len: usize,
|
||||
@ -100,33 +106,21 @@ impl FileDescription for AnonSocket {
|
||||
// corresponding ErrorKind variant.
|
||||
throw_unsup_format!("reading from the write end of a pipe");
|
||||
};
|
||||
if readbuf.borrow().buf.is_empty() {
|
||||
if self.peer_fd().upgrade().is_none() {
|
||||
// Socketpair with no peer and empty buffer.
|
||||
// 0 bytes successfully read indicates end-of-file.
|
||||
return ecx.return_read_success(ptr, &[], 0, dest);
|
||||
} else {
|
||||
if self.is_nonblock {
|
||||
// Non-blocking socketpair with writer and empty buffer.
|
||||
// https://linux.die.net/man/2/read
|
||||
// EAGAIN or EWOULDBLOCK can be returned for socket,
|
||||
// POSIX.1-2001 allows either error to be returned for this case.
|
||||
// Since there is no ErrorKind for EAGAIN, WouldBlock is used.
|
||||
return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
|
||||
} else {
|
||||
// Blocking socketpair with writer and empty buffer.
|
||||
// FIXME: blocking is currently not supported
|
||||
throw_unsup_format!("socketpair/pipe/pipe2 read: blocking isn't supported yet");
|
||||
}
|
||||
}
|
||||
|
||||
if readbuf.borrow().buf.is_empty() && self.is_nonblock {
|
||||
// Non-blocking socketpair with writer and empty buffer.
|
||||
// https://linux.die.net/man/2/read
|
||||
// EAGAIN or EWOULDBLOCK can be returned for socket,
|
||||
// POSIX.1-2001 allows either error to be returned for this case.
|
||||
// Since there is no ErrorKind for EAGAIN, WouldBlock is used.
|
||||
return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
|
||||
}
|
||||
// TODO: We might need to decide what to do if peer_fd is closed when read is blocked.
|
||||
anonsocket_read(self, self.peer_fd().upgrade(), len, ptr, dest, ecx)
|
||||
anonsocket_read(self_ref.downgrade(), len, ptr, dest.clone(), ecx)
|
||||
}
|
||||
|
||||
fn write<'tcx>(
|
||||
&self,
|
||||
_self_ref: &FileDescriptionRef,
|
||||
self_ref: &FileDescriptionRef,
|
||||
_communicate_allowed: bool,
|
||||
ptr: Pointer,
|
||||
len: usize,
|
||||
@ -153,16 +147,11 @@ impl FileDescription for AnonSocket {
|
||||
};
|
||||
let available_space =
|
||||
MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(writebuf.borrow().buf.len());
|
||||
if available_space == 0 {
|
||||
if self.is_nonblock {
|
||||
// Non-blocking socketpair with a full buffer.
|
||||
return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
|
||||
} else {
|
||||
// Blocking socketpair with a full buffer.
|
||||
throw_unsup_format!("socketpair/pipe/pipe2 write: blocking isn't supported yet");
|
||||
}
|
||||
if available_space == 0 && self.is_nonblock {
|
||||
// Non-blocking socketpair with a full buffer.
|
||||
return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
|
||||
}
|
||||
anonsocket_write(available_space, &peer_fd, ptr, len, dest, ecx)
|
||||
anonsocket_write(self_ref.downgrade(), ptr, len, dest.clone(), ecx)
|
||||
}
|
||||
|
||||
fn as_unix(&self) -> &dyn UnixFileDescription {
|
||||
@ -172,81 +161,161 @@ impl FileDescription for AnonSocket {
|
||||
|
||||
/// Write to AnonSocket based on the space available and return the written byte size.
|
||||
fn anonsocket_write<'tcx>(
|
||||
available_space: usize,
|
||||
peer_fd: &FileDescriptionRef,
|
||||
weak_self_ref: WeakFileDescriptionRef,
|
||||
ptr: Pointer,
|
||||
len: usize,
|
||||
dest: &MPlaceTy<'tcx>,
|
||||
dest: MPlaceTy<'tcx>,
|
||||
ecx: &mut MiriInterpCx<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let Some(self_ref) = weak_self_ref.upgrade() else {
|
||||
// FIXME: We should raise a deadlock error if the self_ref upgrade failed.
|
||||
throw_unsup_format!("This will be a deadlock error in future")
|
||||
};
|
||||
let self_anonsocket = self_ref.downcast::<AnonSocket>().unwrap();
|
||||
let Some(peer_fd) = self_anonsocket.peer_fd().upgrade() else {
|
||||
// If the upgrade from Weak to Rc fails, it indicates that all read ends have been
|
||||
// closed.
|
||||
return ecx.set_last_error_and_return(ErrorKind::BrokenPipe, &dest);
|
||||
};
|
||||
let Some(writebuf) = &peer_fd.downcast::<AnonSocket>().unwrap().readbuf else {
|
||||
// FIXME: This should return EBADF, but there's no nice way to do that as there's no
|
||||
// corresponding ErrorKind variant.
|
||||
throw_unsup_format!("writing to the reading end of a pipe")
|
||||
};
|
||||
let mut writebuf = writebuf.borrow_mut();
|
||||
|
||||
// Remember this clock so `read` can synchronize with us.
|
||||
ecx.release_clock(|clock| {
|
||||
writebuf.clock.join(clock);
|
||||
});
|
||||
// Do full write / partial write based on the space available.
|
||||
let actual_write_size = len.min(available_space);
|
||||
let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
|
||||
writebuf.buf.extend(&bytes[..actual_write_size]);
|
||||
let available_space = MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(writebuf.borrow().buf.len());
|
||||
|
||||
// Need to stop accessing peer_fd so that it can be notified.
|
||||
drop(writebuf);
|
||||
if available_space == 0 {
|
||||
// Blocking socketpair with a full buffer.
|
||||
let dest = dest.clone();
|
||||
self_anonsocket.blocked_write_tid.borrow_mut().push(ecx.active_thread());
|
||||
ecx.block_thread(
|
||||
BlockReason::UnnamedSocket,
|
||||
None,
|
||||
callback!(
|
||||
@capture<'tcx> {
|
||||
weak_self_ref: WeakFileDescriptionRef,
|
||||
ptr: Pointer,
|
||||
len: usize,
|
||||
dest: MPlaceTy<'tcx>,
|
||||
}
|
||||
@unblock = |this| {
|
||||
anonsocket_write(weak_self_ref, ptr, len, dest, this)
|
||||
}
|
||||
),
|
||||
);
|
||||
} else {
|
||||
let mut writebuf = writebuf.borrow_mut();
|
||||
// Remember this clock so `read` can synchronize with us.
|
||||
ecx.release_clock(|clock| {
|
||||
writebuf.clock.join(clock);
|
||||
});
|
||||
// Do full write / partial write based on the space available.
|
||||
let actual_write_size = len.min(available_space);
|
||||
let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
|
||||
writebuf.buf.extend(&bytes[..actual_write_size]);
|
||||
|
||||
// Notification should be provided for peer fd as it became readable.
|
||||
// The kernel does this even if the fd was already readable before, so we follow suit.
|
||||
ecx.check_and_update_readiness(peer_fd)?;
|
||||
// Need to stop accessing peer_fd so that it can be notified.
|
||||
drop(writebuf);
|
||||
|
||||
ecx.return_write_success(actual_write_size, dest)
|
||||
// Notification should be provided for peer fd as it became readable.
|
||||
// The kernel does this even if the fd was already readable before, so we follow suit.
|
||||
ecx.check_and_update_readiness(&peer_fd)?;
|
||||
let peer_anonsocket = peer_fd.downcast::<AnonSocket>().unwrap();
|
||||
// Unblock all threads that are currently blocked on peer_fd's read.
|
||||
let waiting_threads = std::mem::take(&mut *peer_anonsocket.blocked_read_tid.borrow_mut());
|
||||
// FIXME: We can randomize the order of unblocking.
|
||||
for thread_id in waiting_threads {
|
||||
ecx.unblock_thread(thread_id, BlockReason::UnnamedSocket)?;
|
||||
}
|
||||
|
||||
return ecx.return_write_success(actual_write_size, &dest);
|
||||
}
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
/// Read from AnonSocket and return the number of bytes read.
|
||||
fn anonsocket_read<'tcx>(
|
||||
anonsocket: &AnonSocket,
|
||||
peer_fd: Option<FileDescriptionRef>,
|
||||
weak_self_ref: WeakFileDescriptionRef,
|
||||
len: usize,
|
||||
ptr: Pointer,
|
||||
dest: &MPlaceTy<'tcx>,
|
||||
dest: MPlaceTy<'tcx>,
|
||||
ecx: &mut MiriInterpCx<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let mut bytes = vec![0; len];
|
||||
let Some(self_ref) = weak_self_ref.upgrade() else {
|
||||
// FIXME: We should raise a deadlock error if the self_ref upgrade failed.
|
||||
throw_unsup_format!("This will be a deadlock error in future")
|
||||
};
|
||||
let self_anonsocket = self_ref.downcast::<AnonSocket>().unwrap();
|
||||
|
||||
let Some(readbuf) = &anonsocket.readbuf else {
|
||||
let Some(readbuf) = &self_anonsocket.readbuf else {
|
||||
// FIXME: This should return EBADF, but there's no nice way to do that as there's no
|
||||
// corresponding ErrorKind variant.
|
||||
throw_unsup_format!("reading from the write end of a pipe")
|
||||
};
|
||||
let mut readbuf = readbuf.borrow_mut();
|
||||
|
||||
// Synchronize with all previous writes to this buffer.
|
||||
// FIXME: this over-synchronizes; a more precise approach would be to
|
||||
// only sync with the writes whose data we will read.
|
||||
ecx.acquire_clock(&readbuf.clock);
|
||||
if readbuf.borrow_mut().buf.is_empty() {
|
||||
if self_anonsocket.peer_fd().upgrade().is_none() {
|
||||
// Socketpair with no peer and empty buffer.
|
||||
// 0 bytes successfully read indicates end-of-file.
|
||||
return ecx.return_read_success(ptr, &[], 0, &dest);
|
||||
} else {
|
||||
// Blocking socketpair with writer and empty buffer.
|
||||
let weak_self_ref = weak_self_ref.clone();
|
||||
self_anonsocket.blocked_read_tid.borrow_mut().push(ecx.active_thread());
|
||||
ecx.block_thread(
|
||||
BlockReason::UnnamedSocket,
|
||||
None,
|
||||
callback!(
|
||||
@capture<'tcx> {
|
||||
weak_self_ref: WeakFileDescriptionRef,
|
||||
len: usize,
|
||||
ptr: Pointer,
|
||||
dest: MPlaceTy<'tcx>,
|
||||
}
|
||||
@unblock = |this| {
|
||||
anonsocket_read(weak_self_ref, len, ptr, dest, this)
|
||||
}
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let mut bytes = vec![0; len];
|
||||
let mut readbuf = readbuf.borrow_mut();
|
||||
// Synchronize with all previous writes to this buffer.
|
||||
// FIXME: this over-synchronizes; a more precise approach would be to
|
||||
// only sync with the writes whose data we will read.
|
||||
ecx.acquire_clock(&readbuf.clock);
|
||||
|
||||
// Do full read / partial read based on the space available.
|
||||
// Conveniently, `read` exists on `VecDeque` and has exactly the desired behavior.
|
||||
let actual_read_size = readbuf.buf.read(&mut bytes[..]).unwrap();
|
||||
// Do full read / partial read based on the space available.
|
||||
// Conveniently, `read` exists on `VecDeque` and has exactly the desired behavior.
|
||||
let actual_read_size = readbuf.buf.read(&mut bytes[..]).unwrap();
|
||||
|
||||
// Need to drop before others can access the readbuf again.
|
||||
drop(readbuf);
|
||||
// Need to drop before others can access the readbuf again.
|
||||
drop(readbuf);
|
||||
|
||||
// A notification should be provided for the peer file description even when it can
|
||||
// only write 1 byte. This implementation is not compliant with the actual Linux kernel
|
||||
// implementation. For optimization reasons, the kernel will only mark the file description
|
||||
// as "writable" when it can write more than a certain number of bytes. Since we
|
||||
// don't know what that *certain number* is, we will provide a notification every time
|
||||
// a read is successful. This might result in our epoll emulation providing more
|
||||
// notifications than the real system.
|
||||
if let Some(peer_fd) = peer_fd {
|
||||
ecx.check_and_update_readiness(&peer_fd)?;
|
||||
// A notification should be provided for the peer file description even when it can
|
||||
// only write 1 byte. This implementation is not compliant with the actual Linux kernel
|
||||
// implementation. For optimization reasons, the kernel will only mark the file description
|
||||
// as "writable" when it can write more than a certain number of bytes. Since we
|
||||
// don't know what that *certain number* is, we will provide a notification every time
|
||||
// a read is successful. This might result in our epoll emulation providing more
|
||||
// notifications than the real system.
|
||||
if let Some(peer_fd) = self_anonsocket.peer_fd().upgrade() {
|
||||
ecx.check_and_update_readiness(&peer_fd)?;
|
||||
let peer_anonsocket = peer_fd.downcast::<AnonSocket>().unwrap();
|
||||
// Unblock all threads that are currently blocked on peer_fd's write.
|
||||
let waiting_threads =
|
||||
std::mem::take(&mut *peer_anonsocket.blocked_write_tid.borrow_mut());
|
||||
// FIXME: We can randomize the order of unblocking.
|
||||
for thread_id in waiting_threads {
|
||||
ecx.unblock_thread(thread_id, BlockReason::UnnamedSocket)?;
|
||||
}
|
||||
};
|
||||
|
||||
return ecx.return_read_success(ptr, &bytes, actual_read_size, &dest);
|
||||
}
|
||||
|
||||
ecx.return_read_success(ptr, &bytes, actual_read_size, dest)
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
impl UnixFileDescription for AnonSocket {
|
||||
@ -360,12 +429,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
readbuf: Some(RefCell::new(Buffer::new())),
|
||||
peer_fd: OnceCell::new(),
|
||||
peer_lost_data: Cell::new(false),
|
||||
blocked_read_tid: RefCell::new(Vec::new()),
|
||||
blocked_write_tid: RefCell::new(Vec::new()),
|
||||
is_nonblock: is_sock_nonblock,
|
||||
});
|
||||
let fd1 = fds.new_ref(AnonSocket {
|
||||
readbuf: Some(RefCell::new(Buffer::new())),
|
||||
peer_fd: OnceCell::new(),
|
||||
peer_lost_data: Cell::new(false),
|
||||
blocked_read_tid: RefCell::new(Vec::new()),
|
||||
blocked_write_tid: RefCell::new(Vec::new()),
|
||||
is_nonblock: is_sock_nonblock,
|
||||
});
|
||||
|
||||
@ -424,12 +497,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
readbuf: Some(RefCell::new(Buffer::new())),
|
||||
peer_fd: OnceCell::new(),
|
||||
peer_lost_data: Cell::new(false),
|
||||
blocked_read_tid: RefCell::new(Vec::new()),
|
||||
blocked_write_tid: RefCell::new(Vec::new()),
|
||||
is_nonblock,
|
||||
});
|
||||
let fd1 = fds.new_ref(AnonSocket {
|
||||
readbuf: None,
|
||||
peer_fd: OnceCell::new(),
|
||||
peer_lost_data: Cell::new(false),
|
||||
blocked_read_tid: RefCell::new(Vec::new()),
|
||||
blocked_write_tid: RefCell::new(Vec::new()),
|
||||
is_nonblock,
|
||||
});
|
||||
|
||||
|
@ -22,14 +22,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
match link_name.as_str() {
|
||||
// Allocation
|
||||
"posix_memalign" => {
|
||||
let [memptr, align, size] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [memptr, align, size] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let result = this.posix_memalign(memptr, align, size)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"aligned_alloc" => {
|
||||
let [align, size] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [align, size] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let res = this.aligned_alloc(align, size)?;
|
||||
this.write_pointer(res, dest)?;
|
||||
}
|
||||
|
@ -108,50 +108,42 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
match link_name.as_str() {
|
||||
// Environment related shims
|
||||
"GetEnvironmentVariableW" => {
|
||||
let [name, buf, size] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [name, buf, size] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let result = this.GetEnvironmentVariableW(name, buf, size)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"SetEnvironmentVariableW" => {
|
||||
let [name, value] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [name, value] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let result = this.SetEnvironmentVariableW(name, value)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"GetEnvironmentStringsW" => {
|
||||
let [] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let result = this.GetEnvironmentStringsW()?;
|
||||
this.write_pointer(result, dest)?;
|
||||
}
|
||||
"FreeEnvironmentStringsW" => {
|
||||
let [env_block] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [env_block] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let result = this.FreeEnvironmentStringsW(env_block)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"GetCurrentDirectoryW" => {
|
||||
let [size, buf] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [size, buf] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let result = this.GetCurrentDirectoryW(size, buf)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"SetCurrentDirectoryW" => {
|
||||
let [path] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [path] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let result = this.SetCurrentDirectoryW(path)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"GetUserProfileDirectoryW" => {
|
||||
let [token, buf, size] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [token, buf, size] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let result = this.GetUserProfileDirectoryW(token, buf, size)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"GetCurrentProcessId" => {
|
||||
let [] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let result = this.GetCurrentProcessId()?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
@ -257,8 +249,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
// Allocation
|
||||
"HeapAlloc" => {
|
||||
let [handle, flags, size] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [handle, flags, size] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
this.read_target_isize(handle)?;
|
||||
let flags = this.read_scalar(flags)?.to_u32()?;
|
||||
let size = this.read_target_usize(size)?;
|
||||
@ -281,8 +272,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.write_pointer(ptr, dest)?;
|
||||
}
|
||||
"HeapFree" => {
|
||||
let [handle, flags, ptr] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [handle, flags, ptr] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
this.read_target_isize(handle)?;
|
||||
this.read_scalar(flags)?.to_u32()?;
|
||||
let ptr = this.read_pointer(ptr)?;
|
||||
@ -314,8 +304,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.write_pointer(new_ptr, dest)?;
|
||||
}
|
||||
"LocalFree" => {
|
||||
let [ptr] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [ptr] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let ptr = this.read_pointer(ptr)?;
|
||||
// "If the hMem parameter is NULL, LocalFree ignores the parameter and returns NULL."
|
||||
// (https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localfree)
|
||||
@ -327,14 +316,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
// errno
|
||||
"SetLastError" => {
|
||||
let [error] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [error] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let error = this.read_scalar(error)?;
|
||||
this.set_last_error(error)?;
|
||||
}
|
||||
"GetLastError" => {
|
||||
let [] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let last_error = this.get_last_error()?;
|
||||
this.write_scalar(last_error, dest)?;
|
||||
}
|
||||
@ -342,8 +329,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Querying system information
|
||||
"GetSystemInfo" => {
|
||||
// Also called from `page_size` crate.
|
||||
let [system_info] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [system_info] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let system_info =
|
||||
this.deref_pointer_as(system_info, this.windows_ty_layout("SYSTEM_INFO"))?;
|
||||
// Initialize with `0`.
|
||||
@ -366,22 +352,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// This just creates a key; Windows does not natively support TLS destructors.
|
||||
|
||||
// Create key and return it.
|
||||
let [] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let key = this.machine.tls.create_tls_key(None, dest.layout.size)?;
|
||||
this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
|
||||
}
|
||||
"TlsGetValue" => {
|
||||
let [key] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [key] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let key = u128::from(this.read_scalar(key)?.to_u32()?);
|
||||
let active_thread = this.active_thread();
|
||||
let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
|
||||
this.write_scalar(ptr, dest)?;
|
||||
}
|
||||
"TlsSetValue" => {
|
||||
let [key, new_ptr] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [key, new_ptr] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let key = u128::from(this.read_scalar(key)?.to_u32()?);
|
||||
let active_thread = this.active_thread();
|
||||
let new_data = this.read_scalar(new_ptr)?;
|
||||
@ -401,8 +384,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
// Access to command-line arguments
|
||||
"GetCommandLineW" => {
|
||||
let [] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
this.write_pointer(
|
||||
this.machine.cmd_line.expect("machine must be initialized"),
|
||||
dest,
|
||||
@ -412,27 +394,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Time related shims
|
||||
"GetSystemTimeAsFileTime" | "GetSystemTimePreciseAsFileTime" => {
|
||||
#[allow(non_snake_case)]
|
||||
let [LPFILETIME] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [LPFILETIME] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
this.GetSystemTimeAsFileTime(link_name.as_str(), LPFILETIME)?;
|
||||
}
|
||||
"QueryPerformanceCounter" => {
|
||||
#[allow(non_snake_case)]
|
||||
let [lpPerformanceCount] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [lpPerformanceCount] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let result = this.QueryPerformanceCounter(lpPerformanceCount)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"QueryPerformanceFrequency" => {
|
||||
#[allow(non_snake_case)]
|
||||
let [lpFrequency] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [lpFrequency] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let result = this.QueryPerformanceFrequency(lpFrequency)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"Sleep" => {
|
||||
let [timeout] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [timeout] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
|
||||
this.Sleep(timeout)?;
|
||||
}
|
||||
@ -456,8 +434,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.InitOnceBeginInitialize(ptr, flags, pending, context, dest)?;
|
||||
}
|
||||
"InitOnceComplete" => {
|
||||
let [ptr, flags, context] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [ptr, flags, context] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let result = this.InitOnceComplete(ptr, flags, context)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
@ -468,14 +445,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.WaitOnAddress(ptr_op, compare_op, size_op, timeout_op, dest)?;
|
||||
}
|
||||
"WakeByAddressSingle" => {
|
||||
let [ptr_op] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [ptr_op] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
|
||||
this.WakeByAddressSingle(ptr_op)?;
|
||||
}
|
||||
"WakeByAddressAll" => {
|
||||
let [ptr_op] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [ptr_op] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
|
||||
this.WakeByAddressAll(ptr_op)?;
|
||||
}
|
||||
@ -483,8 +458,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Dynamic symbol loading
|
||||
"GetProcAddress" => {
|
||||
#[allow(non_snake_case)]
|
||||
let [hModule, lpProcName] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [hModule, lpProcName] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
this.read_target_isize(hModule)?;
|
||||
let name = this.read_c_str(this.read_pointer(lpProcName)?)?;
|
||||
if let Ok(name) = str::from_utf8(name)
|
||||
@ -508,15 +482,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.write_scalar(Handle::Thread(thread_id).to_scalar(this), dest)?;
|
||||
}
|
||||
"WaitForSingleObject" => {
|
||||
let [handle, timeout] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [handle, timeout] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
|
||||
let ret = this.WaitForSingleObject(handle, timeout)?;
|
||||
this.write_scalar(ret, dest)?;
|
||||
}
|
||||
"GetCurrentThread" => {
|
||||
let [] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
|
||||
this.write_scalar(
|
||||
Handle::Pseudo(PseudoHandle::CurrentThread).to_scalar(this),
|
||||
@ -524,8 +496,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
)?;
|
||||
}
|
||||
"SetThreadDescription" => {
|
||||
let [handle, name] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [handle, name] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
|
||||
let handle = this.read_scalar(handle)?;
|
||||
let name = this.read_wide_str(this.read_pointer(name)?)?;
|
||||
@ -549,8 +520,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"GetThreadDescription" => {
|
||||
let [handle, name_ptr] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [handle, name_ptr] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
|
||||
let handle = this.read_scalar(handle)?;
|
||||
let name_ptr = this.deref_pointer(name_ptr)?; // the pointer where we should store the ptr to the name
|
||||
@ -581,16 +551,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
// Miscellaneous
|
||||
"ExitProcess" => {
|
||||
let [code] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [code] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let code = this.read_scalar(code)?.to_u32()?;
|
||||
throw_machine_stop!(TerminationInfo::Exit { code: code.into(), leak_check: false });
|
||||
}
|
||||
"SystemFunction036" => {
|
||||
// used by getrandom 0.1
|
||||
// This is really 'RtlGenRandom'.
|
||||
let [ptr, len] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [ptr, len] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let ptr = this.read_pointer(ptr)?;
|
||||
let len = this.read_scalar(len)?.to_u32()?;
|
||||
this.gen_random(ptr, len.into())?;
|
||||
@ -598,8 +566,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
"ProcessPrng" => {
|
||||
// used by `std`
|
||||
let [ptr, len] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [ptr, len] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let ptr = this.read_pointer(ptr)?;
|
||||
let len = this.read_target_usize(len)?;
|
||||
this.gen_random(ptr, len)?;
|
||||
@ -642,8 +609,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
"GetConsoleScreenBufferInfo" => {
|
||||
// `term` needs this, so we fake it.
|
||||
let [console, buffer_info] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [console, buffer_info] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
this.read_target_isize(console)?;
|
||||
// FIXME: this should use deref_pointer_as, but CONSOLE_SCREEN_BUFFER_INFO is not in std
|
||||
this.deref_pointer(buffer_info)?;
|
||||
@ -652,8 +618,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
"GetStdHandle" => {
|
||||
let [which] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [which] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let which = this.read_scalar(which)?.to_i32()?;
|
||||
// We just make this the identity function, so we know later in `NtWriteFile` which
|
||||
// one it is. This is very fake, but libtest needs it so we cannot make it a
|
||||
@ -662,16 +627,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.write_scalar(Scalar::from_target_isize(which.into(), this), dest)?;
|
||||
}
|
||||
"CloseHandle" => {
|
||||
let [handle] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [handle] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
|
||||
let ret = this.CloseHandle(handle)?;
|
||||
|
||||
this.write_scalar(ret, dest)?;
|
||||
}
|
||||
"GetModuleFileNameW" => {
|
||||
let [handle, filename, size] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [handle, filename, size] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
this.check_no_isolation("`GetModuleFileNameW`")?;
|
||||
|
||||
let handle = this.read_target_usize(handle)?;
|
||||
@ -740,16 +703,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
|
||||
// These shims are enabled only when the caller is in the standard library.
|
||||
"GetProcessHeap" if this.frame_in_std() => {
|
||||
let [] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
// Just fake a HANDLE
|
||||
// It's fine to not use the Handle type here because its a stub
|
||||
this.write_int(1, dest)?;
|
||||
}
|
||||
"GetModuleHandleA" if this.frame_in_std() => {
|
||||
#[allow(non_snake_case)]
|
||||
let [_lpModuleName] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [_lpModuleName] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
// We need to return something non-null here to make `compat_fn!` work.
|
||||
this.write_int(1, dest)?;
|
||||
}
|
||||
@ -761,8 +722,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
"GetConsoleMode" if this.frame_in_std() => {
|
||||
let [console, mode] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [console, mode] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
this.read_target_isize(console)?;
|
||||
this.deref_pointer(mode)?;
|
||||
// Indicate an error.
|
||||
@ -770,29 +730,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
"GetFileType" if this.frame_in_std() => {
|
||||
#[allow(non_snake_case)]
|
||||
let [_hFile] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [_hFile] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
// Return unknown file type.
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
"AddVectoredExceptionHandler" if this.frame_in_std() => {
|
||||
#[allow(non_snake_case)]
|
||||
let [_First, _Handler] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [_First, _Handler] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
// Any non zero value works for the stdlib. This is just used for stack overflows anyway.
|
||||
this.write_int(1, dest)?;
|
||||
}
|
||||
"SetThreadStackGuarantee" if this.frame_in_std() => {
|
||||
#[allow(non_snake_case)]
|
||||
let [_StackSizeInBytes] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [_StackSizeInBytes] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
// Any non zero value works for the stdlib. This is just used for stack overflows anyway.
|
||||
this.write_int(1, dest)?;
|
||||
}
|
||||
// this is only callable from std because we know that std ignores the return value
|
||||
"SwitchToThread" if this.frame_in_std() => {
|
||||
let [] =
|
||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||
|
||||
this.yield_active_thread();
|
||||
|
||||
@ -811,8 +767,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
);
|
||||
}
|
||||
// This function looks and behaves excatly like miri_start_unwind.
|
||||
let [payload] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [payload] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
this.handle_miri_start_unwind(payload)?;
|
||||
return interp_ok(EmulateItemResult::NeedsUnwind);
|
||||
}
|
||||
|
@ -26,8 +26,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// `state` with the corresponding 128-bit key of `key`.
|
||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdec_si128
|
||||
"aesdec" | "aesdec.256" | "aesdec.512" => {
|
||||
let [state, key] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [state, key] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
aes_round(this, state, key, dest, |state, key| {
|
||||
let key = aes::Block::from(key.to_le_bytes());
|
||||
let mut state = aes::Block::from(state.to_le_bytes());
|
||||
@ -43,8 +42,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// `state` with the corresponding 128-bit key of `key`.
|
||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdeclast_si128
|
||||
"aesdeclast" | "aesdeclast.256" | "aesdeclast.512" => {
|
||||
let [state, key] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [state, key] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
aes_round(this, state, key, dest, |state, key| {
|
||||
let mut state = aes::Block::from(state.to_le_bytes());
|
||||
@ -68,8 +66,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// `state` with the corresponding 128-bit key of `key`.
|
||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenc_si128
|
||||
"aesenc" | "aesenc.256" | "aesenc.512" => {
|
||||
let [state, key] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [state, key] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
aes_round(this, state, key, dest, |state, key| {
|
||||
let key = aes::Block::from(key.to_le_bytes());
|
||||
let mut state = aes::Block::from(state.to_le_bytes());
|
||||
@ -85,8 +82,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// `state` with the corresponding 128-bit key of `key`.
|
||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenclast_si128
|
||||
"aesenclast" | "aesenclast.256" | "aesenclast.512" => {
|
||||
let [state, key] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [state, key] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
aes_round(this, state, key, dest, |state, key| {
|
||||
let mut state = aes::Block::from(state.to_le_bytes());
|
||||
// `aes::hazmat::cipher_round` does the following operations:
|
||||
|
@ -33,8 +33,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// matches the IEEE min/max operations, while x86 has different
|
||||
// semantics.
|
||||
"min.ps.256" | "max.ps.256" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let which = match unprefixed_name {
|
||||
"min.ps.256" => FloatBinOp::Min,
|
||||
@ -46,8 +45,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
// Used to implement _mm256_min_pd and _mm256_max_pd functions.
|
||||
"min.pd.256" | "max.pd.256" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let which = match unprefixed_name {
|
||||
"min.pd.256" => FloatBinOp::Min,
|
||||
@ -60,16 +58,14 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Used to implement the _mm256_round_ps function.
|
||||
// Rounds the elements of `op` according to `rounding`.
|
||||
"round.ps.256" => {
|
||||
let [op, rounding] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [op, rounding] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
round_all::<rustc_apfloat::ieee::Single>(this, op, rounding, dest)?;
|
||||
}
|
||||
// Used to implement the _mm256_round_pd function.
|
||||
// Rounds the elements of `op` according to `rounding`.
|
||||
"round.pd.256" => {
|
||||
let [op, rounding] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [op, rounding] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
round_all::<rustc_apfloat::ieee::Double>(this, op, rounding, dest)?;
|
||||
}
|
||||
@ -88,8 +84,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
// Used to implement the _mm256_dp_ps function.
|
||||
"dp.ps.256" => {
|
||||
let [left, right, imm] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
conditional_dot_product(this, left, right, imm, dest)?;
|
||||
}
|
||||
@ -97,8 +92,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Horizontally add/subtract adjacent floating point values
|
||||
// in `left` and `right`.
|
||||
"hadd.ps.256" | "hadd.pd.256" | "hsub.ps.256" | "hsub.pd.256" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let which = match unprefixed_name {
|
||||
"hadd.ps.256" | "hadd.pd.256" => mir::BinOp::Add,
|
||||
@ -113,8 +107,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// and `right`. For each component, returns 0 if false or u32::MAX
|
||||
// if true.
|
||||
"cmp.ps.256" => {
|
||||
let [left, right, imm] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let which =
|
||||
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
|
||||
@ -126,8 +119,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// and `right`. For each component, returns 0 if false or u64::MAX
|
||||
// if true.
|
||||
"cmp.pd.256" => {
|
||||
let [left, right, imm] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let which =
|
||||
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
|
||||
@ -156,8 +148,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// sequence of 4-element arrays, and we shuffle each of these arrays, where
|
||||
// `control` determines which element of the current `data` array is written.
|
||||
"vpermilvar.ps" | "vpermilvar.ps.256" => {
|
||||
let [data, control] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [data, control] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (data, data_len) = this.project_to_simd(data)?;
|
||||
let (control, control_len) = this.project_to_simd(control)?;
|
||||
@ -190,8 +181,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// where `right` determines which element of the current `left` array is
|
||||
// written.
|
||||
"vpermilvar.pd" | "vpermilvar.pd.256" => {
|
||||
let [data, control] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [data, control] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (data, data_len) = this.project_to_simd(data)?;
|
||||
let (control, control_len) = this.project_to_simd(control)?;
|
||||
@ -223,8 +213,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// For each 128-bit element of `dest`, copies one from `left`, `right` or
|
||||
// zero, according to `imm`.
|
||||
"vperm2f128.ps.256" | "vperm2f128.pd.256" | "vperm2f128.si.256" => {
|
||||
let [left, right, imm] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
assert_eq!(dest.layout, left.layout);
|
||||
assert_eq!(dest.layout, right.layout);
|
||||
@ -267,8 +256,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// is one, it is loaded from `ptr.wrapping_add(i)`, otherwise zero is
|
||||
// loaded.
|
||||
"maskload.ps" | "maskload.pd" | "maskload.ps.256" | "maskload.pd.256" => {
|
||||
let [ptr, mask] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [ptr, mask] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
mask_load(this, ptr, mask, dest)?;
|
||||
}
|
||||
@ -278,8 +266,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// is one, it is stored into `ptr.wapping_add(i)`.
|
||||
// Unlike SSE2's _mm_maskmoveu_si128, these are not non-temporal stores.
|
||||
"maskstore.ps" | "maskstore.pd" | "maskstore.ps.256" | "maskstore.pd.256" => {
|
||||
let [ptr, mask, value] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [ptr, mask, value] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
mask_store(this, ptr, mask, value)?;
|
||||
}
|
||||
@ -289,8 +276,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// the data crosses a cache line, but for Miri this is just a regular
|
||||
// unaligned read.
|
||||
"ldu.dq.256" => {
|
||||
let [src_ptr] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [src_ptr] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let src_ptr = this.read_pointer(src_ptr)?;
|
||||
let dest = dest.force_mplace(this)?;
|
||||
|
||||
@ -302,8 +288,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Tests `op & mask == 0`, `op & mask == mask` or
|
||||
// `op & mask != 0 && op & mask != mask`
|
||||
"ptestz.256" | "ptestc.256" | "ptestnzc.256" => {
|
||||
let [op, mask] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [op, mask] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (all_zero, masked_set) = test_bits_masked(this, op, mask)?;
|
||||
let res = match unprefixed_name {
|
||||
@ -326,8 +311,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
"vtestz.pd.256" | "vtestc.pd.256" | "vtestnzc.pd.256" | "vtestz.pd" | "vtestc.pd"
|
||||
| "vtestnzc.pd" | "vtestz.ps.256" | "vtestc.ps.256" | "vtestnzc.ps.256"
|
||||
| "vtestz.ps" | "vtestc.ps" | "vtestnzc.ps" => {
|
||||
let [op, mask] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [op, mask] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (direct, negated) = test_high_bits_masked(this, op, mask)?;
|
||||
let res = match unprefixed_name {
|
||||
|
@ -36,8 +36,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Horizontally add / add with saturation / subtract adjacent 16/32-bit
|
||||
// integer values in `left` and `right`.
|
||||
"phadd.w" | "phadd.sw" | "phadd.d" | "phsub.w" | "phsub.sw" | "phsub.d" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (which, saturating) = match unprefixed_name {
|
||||
"phadd.w" | "phadd.d" => (mir::BinOp::Add, false),
|
||||
@ -115,8 +114,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// intermediate signed 32-bit integers. Horizontally add adjacent pairs of
|
||||
// intermediate 32-bit integers, and pack the results in `dest`.
|
||||
"pmadd.wd" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (left, left_len) = this.project_to_simd(left)?;
|
||||
let (right, right_len) = this.project_to_simd(right)?;
|
||||
@ -152,8 +150,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// the saturating sum of the products with indices `2*i` and `2*i+1`
|
||||
// produces the output at index `i`.
|
||||
"pmadd.ub.sw" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (left, left_len) = this.project_to_simd(left)?;
|
||||
let (right, right_len) = this.project_to_simd(right)?;
|
||||
@ -187,8 +184,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// is one, it is loaded from `ptr.wrapping_add(i)`, otherwise zero is
|
||||
// loaded.
|
||||
"maskload.d" | "maskload.q" | "maskload.d.256" | "maskload.q.256" => {
|
||||
let [ptr, mask] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [ptr, mask] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
mask_load(this, ptr, mask, dest)?;
|
||||
}
|
||||
@ -198,8 +194,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// is one, it is stored into `ptr.wapping_add(i)`.
|
||||
// Unlike SSE2's _mm_maskmoveu_si128, these are not non-temporal stores.
|
||||
"maskstore.d" | "maskstore.q" | "maskstore.d.256" | "maskstore.q.256" => {
|
||||
let [ptr, mask, value] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [ptr, mask, value] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
mask_store(this, ptr, mask, value)?;
|
||||
}
|
||||
@ -210,8 +205,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// offsets specified in `imm`.
|
||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_mpsadbw_epu8
|
||||
"mpsadbw" => {
|
||||
let [left, right, imm] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
mpsadbw(this, left, right, imm, dest)?;
|
||||
}
|
||||
@ -222,8 +216,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// 1 and then taking the bits `1..=16`.
|
||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_mulhrs_epi16
|
||||
"pmul.hr.sw" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
pmulhrsw(this, left, right, dest)?;
|
||||
}
|
||||
@ -231,8 +224,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Converts two 16-bit integer vectors to a single 8-bit integer
|
||||
// vector with signed saturation.
|
||||
"packsswb" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
packsswb(this, left, right, dest)?;
|
||||
}
|
||||
@ -240,8 +232,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Converts two 32-bit integer vectors to a single 16-bit integer
|
||||
// vector with signed saturation.
|
||||
"packssdw" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
packssdw(this, left, right, dest)?;
|
||||
}
|
||||
@ -249,8 +240,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Converts two 16-bit signed integer vectors to a single 8-bit
|
||||
// unsigned integer vector with saturation.
|
||||
"packuswb" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
packuswb(this, left, right, dest)?;
|
||||
}
|
||||
@ -258,8 +248,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Concatenates two 32-bit signed integer vectors and converts
|
||||
// the result to a 16-bit unsigned integer vector with saturation.
|
||||
"packusdw" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
packusdw(this, left, right, dest)?;
|
||||
}
|
||||
@ -268,8 +257,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Shuffles `left` using the three low bits of each element of `right`
|
||||
// as indices.
|
||||
"permd" | "permps" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (left, left_len) = this.project_to_simd(left)?;
|
||||
let (right, right_len) = this.project_to_simd(right)?;
|
||||
@ -289,8 +277,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Used to implement the _mm256_permute2x128_si256 function.
|
||||
// Shuffles 128-bit blocks of `a` and `b` using `imm` as pattern.
|
||||
"vperm2i128" => {
|
||||
let [left, right, imm] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
assert_eq!(left.layout.size.bits(), 256);
|
||||
assert_eq!(right.layout.size.bits(), 256);
|
||||
@ -327,8 +314,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// in `dest`.
|
||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_sad_epu8
|
||||
"psad.bw" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (left, left_len) = this.project_to_simd(left)?;
|
||||
let (right, right_len) = this.project_to_simd(right)?;
|
||||
@ -360,8 +346,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Shuffles bytes from `left` using `right` as pattern.
|
||||
// Each 128-bit block is shuffled independently.
|
||||
"pshuf.b" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (left, left_len) = this.project_to_simd(left)?;
|
||||
let (right, right_len) = this.project_to_simd(right)?;
|
||||
@ -392,8 +377,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// is writen to the corresponding output element.
|
||||
// Basically, we multiply `left` with `right.signum()`.
|
||||
"psign.b" | "psign.w" | "psign.d" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
psign(this, left, right, dest)?;
|
||||
}
|
||||
@ -407,8 +391,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// is copied to remaining bits.
|
||||
"psll.w" | "psrl.w" | "psra.w" | "psll.d" | "psrl.d" | "psra.d" | "psll.q"
|
||||
| "psrl.q" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let which = match unprefixed_name {
|
||||
"psll.w" | "psll.d" | "psll.q" => ShiftOp::Left,
|
||||
@ -423,8 +406,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// (except _mm{,256}_srav_epi64, which are not available in AVX2).
|
||||
"psllv.d" | "psllv.d.256" | "psllv.q" | "psllv.q.256" | "psrlv.d" | "psrlv.d.256"
|
||||
| "psrlv.q" | "psrlv.q.256" | "psrav.d" | "psrav.d.256" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let which = match unprefixed_name {
|
||||
"psllv.d" | "psllv.d.256" | "psllv.q" | "psllv.q.256" => ShiftOp::Left,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use rustc_span::Symbol;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_span::Symbol;
|
||||
use rustc_target::callconv::{Conv, FnAbi};
|
||||
|
||||
use crate::*;
|
||||
@ -34,8 +34,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
return interp_ok(EmulateItemResult::NotSupported);
|
||||
}
|
||||
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let left = this.read_scalar(left)?;
|
||||
let right = this.read_scalar(right)?;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use rustc_span::Symbol;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_span::Symbol;
|
||||
use rustc_target::callconv::{Conv, FnAbi};
|
||||
|
||||
use crate::*;
|
||||
@ -30,16 +30,14 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// See `affine_transform` for details.
|
||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8affine_
|
||||
"vgf2p8affineqb.128" | "vgf2p8affineqb.256" | "vgf2p8affineqb.512" => {
|
||||
let [left, right, imm8] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right, imm8] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
affine_transform(this, left, right, imm8, dest, /* inverse */ false)?;
|
||||
}
|
||||
// Used to implement the `_mm{, 256, 512}_gf2p8affineinv_epi64_epi8` functions.
|
||||
// See `affine_transform` for details.
|
||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8affineinv
|
||||
"vgf2p8affineinvqb.128" | "vgf2p8affineinvqb.256" | "vgf2p8affineinvqb.512" => {
|
||||
let [left, right, imm8] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right, imm8] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
affine_transform(this, left, right, imm8, dest, /* inverse */ true)?;
|
||||
}
|
||||
// Used to implement the `_mm{, 256, 512}_gf2p8mul_epi8` functions.
|
||||
@ -48,8 +46,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// polynomial representation with the reduction polynomial x^8 + x^4 + x^3 + x + 1.
|
||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8mul
|
||||
"vgf2p8mulb.128" | "vgf2p8mulb.256" | "vgf2p8mulb.512" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let (left, left_len) = this.project_to_simd(left)?;
|
||||
let (right, right_len) = this.project_to_simd(right)?;
|
||||
let (dest, dest_len) = this.project_to_simd(dest)?;
|
||||
|
@ -68,8 +68,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
if is_u64 && this.tcx.sess.target.arch != "x86_64" {
|
||||
return interp_ok(EmulateItemResult::NotSupported);
|
||||
}
|
||||
let [c_in, a, b, out] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [c_in, a, b, out] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let out = this.deref_pointer_as(
|
||||
out,
|
||||
if is_u64 { this.machine.layouts.u64 } else { this.machine.layouts.u32 },
|
||||
@ -105,8 +104,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
len = 8;
|
||||
}
|
||||
|
||||
let [left, right, imm] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
pclmulqdq(this, left, right, imm, dest, len)?;
|
||||
}
|
||||
|
@ -52,8 +52,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
match unprefixed_name {
|
||||
// Used to implement the _mm_sha256rnds2_epu32 function.
|
||||
"256rnds2" => {
|
||||
let [a, b, k] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [a, b, k] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (a_reg, a_len) = this.project_to_simd(a)?;
|
||||
let (b_reg, b_len) = this.project_to_simd(b)?;
|
||||
@ -74,8 +73,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
// Used to implement the _mm_sha256msg1_epu32 function.
|
||||
"256msg1" => {
|
||||
let [a, b] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [a, b] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (a_reg, a_len) = this.project_to_simd(a)?;
|
||||
let (b_reg, b_len) = this.project_to_simd(b)?;
|
||||
@ -93,8 +91,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
// Used to implement the _mm_sha256msg2_epu32 function.
|
||||
"256msg2" => {
|
||||
let [a, b] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [a, b] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (a_reg, a_len) = this.project_to_simd(a)?;
|
||||
let (b_reg, b_len) = this.project_to_simd(b)?;
|
||||
|
@ -33,8 +33,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Performs the operations on the first component of `left` and
|
||||
// `right` and copies the remaining components from `left`.
|
||||
"min.ss" | "max.ss" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let which = match unprefixed_name {
|
||||
"min.ss" => FloatBinOp::Min,
|
||||
@ -50,8 +49,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// matches the IEEE min/max operations, while x86 has different
|
||||
// semantics.
|
||||
"min.ps" | "max.ps" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let which = match unprefixed_name {
|
||||
"min.ps" => FloatBinOp::Min,
|
||||
@ -97,8 +95,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_ss are SSE functions
|
||||
// with hard-coded operations.
|
||||
"cmp.ss" => {
|
||||
let [left, right, imm] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let which =
|
||||
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
|
||||
@ -114,8 +111,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_ps are SSE functions
|
||||
// with hard-coded operations.
|
||||
"cmp.ps" => {
|
||||
let [left, right, imm] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let which =
|
||||
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
|
||||
@ -128,8 +124,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
"comieq.ss" | "comilt.ss" | "comile.ss" | "comigt.ss" | "comige.ss" | "comineq.ss"
|
||||
| "ucomieq.ss" | "ucomilt.ss" | "ucomile.ss" | "ucomigt.ss" | "ucomige.ss"
|
||||
| "ucomineq.ss" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (left, left_len) = this.project_to_simd(left)?;
|
||||
let (right, right_len) = this.project_to_simd(right)?;
|
||||
@ -185,8 +180,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// are copied from `left`.
|
||||
// https://www.felixcloutier.com/x86/cvtsi2ss
|
||||
"cvtsi2ss" | "cvtsi642ss" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (left, left_len) = this.project_to_simd(left)?;
|
||||
let (dest, dest_len) = this.project_to_simd(dest)?;
|
||||
|
@ -40,8 +40,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// intermediate signed 32-bit integers. Horizontally add adjacent pairs of
|
||||
// intermediate 32-bit integers, and pack the results in `dest`.
|
||||
"pmadd.wd" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (left, left_len) = this.project_to_simd(left)?;
|
||||
let (right, right_len) = this.project_to_simd(right)?;
|
||||
@ -79,8 +78,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
//
|
||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sad_epu8
|
||||
"psad.bw" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (left, left_len) = this.project_to_simd(left)?;
|
||||
let (right, right_len) = this.project_to_simd(right)?;
|
||||
@ -118,8 +116,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// is copied to remaining bits.
|
||||
"psll.w" | "psrl.w" | "psra.w" | "psll.d" | "psrl.d" | "psra.d" | "psll.q"
|
||||
| "psrl.q" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let which = match unprefixed_name {
|
||||
"psll.w" | "psll.d" | "psll.q" => ShiftOp::Left,
|
||||
@ -171,8 +168,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Converts two 16-bit integer vectors to a single 8-bit integer
|
||||
// vector with signed saturation.
|
||||
"packsswb.128" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
packsswb(this, left, right, dest)?;
|
||||
}
|
||||
@ -180,8 +176,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Converts two 16-bit signed integer vectors to a single 8-bit
|
||||
// unsigned integer vector with saturation.
|
||||
"packuswb.128" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
packuswb(this, left, right, dest)?;
|
||||
}
|
||||
@ -189,8 +184,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Converts two 32-bit integer vectors to a single 16-bit integer
|
||||
// vector with signed saturation.
|
||||
"packssdw.128" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
packssdw(this, left, right, dest)?;
|
||||
}
|
||||
@ -200,8 +194,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// matches the IEEE min/max operations, while x86 has different
|
||||
// semantics.
|
||||
"min.sd" | "max.sd" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let which = match unprefixed_name {
|
||||
"min.sd" => FloatBinOp::Min,
|
||||
@ -217,8 +210,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// matches the IEEE min/max operations, while x86 has different
|
||||
// semantics.
|
||||
"min.pd" | "max.pd" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let which = match unprefixed_name {
|
||||
"min.pd" => FloatBinOp::Min,
|
||||
@ -237,8 +229,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_sd are SSE2 functions
|
||||
// with hard-coded operations.
|
||||
"cmp.sd" => {
|
||||
let [left, right, imm] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let which =
|
||||
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
|
||||
@ -254,8 +245,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_pd are SSE2 functions
|
||||
// with hard-coded operations.
|
||||
"cmp.pd" => {
|
||||
let [left, right, imm] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let which =
|
||||
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
|
||||
@ -268,8 +258,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
"comieq.sd" | "comilt.sd" | "comile.sd" | "comigt.sd" | "comige.sd" | "comineq.sd"
|
||||
| "ucomieq.sd" | "ucomilt.sd" | "ucomile.sd" | "ucomigt.sd" | "ucomige.sd"
|
||||
| "ucomineq.sd" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (left, left_len) = this.project_to_simd(left)?;
|
||||
let (right, right_len) = this.project_to_simd(right)?;
|
||||
@ -323,8 +312,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Converts the first f64/f32 from `right` to f32/f64 and copies
|
||||
// the remaining elements from `left`
|
||||
"cvtsd2ss" | "cvtss2sd" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (left, left_len) = this.project_to_simd(left)?;
|
||||
let (right, _) = this.project_to_simd(right)?;
|
||||
|
@ -25,8 +25,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Horizontally add/subtract adjacent floating point values
|
||||
// in `left` and `right`.
|
||||
"hadd.ps" | "hadd.pd" | "hsub.ps" | "hsub.pd" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let which = match unprefixed_name {
|
||||
"hadd.ps" | "hadd.pd" => mir::BinOp::Add,
|
||||
@ -42,8 +41,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// the data crosses a cache line, but for Miri this is just a regular
|
||||
// unaligned read.
|
||||
"ldu.dq" => {
|
||||
let [src_ptr] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [src_ptr] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let src_ptr = this.read_pointer(src_ptr)?;
|
||||
let dest = dest.force_mplace(this)?;
|
||||
|
||||
|
@ -27,8 +27,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// bits `4..=5` if `imm`, and `i`th bit specifies whether element
|
||||
// `i` is zeroed.
|
||||
"insertps" => {
|
||||
let [left, right, imm] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (left, left_len) = this.project_to_simd(left)?;
|
||||
let (right, right_len) = this.project_to_simd(right)?;
|
||||
@ -63,8 +62,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Concatenates two 32-bit signed integer vectors and converts
|
||||
// the result to a 16-bit unsigned integer vector with saturation.
|
||||
"packusdw" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
packusdw(this, left, right, dest)?;
|
||||
}
|
||||
@ -74,8 +72,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// products, and conditionally stores the sum in `dest` using the low
|
||||
// 4 bits of `imm`.
|
||||
"dpps" | "dppd" => {
|
||||
let [left, right, imm] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
conditional_dot_product(this, left, right, imm, dest)?;
|
||||
}
|
||||
@ -83,16 +80,14 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// functions. Rounds the first element of `right` according to `rounding`
|
||||
// and copies the remaining elements from `left`.
|
||||
"round.ss" => {
|
||||
let [left, right, rounding] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right, rounding] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
round_first::<rustc_apfloat::ieee::Single>(this, left, right, rounding, dest)?;
|
||||
}
|
||||
// Used to implement the _mm_floor_ps, _mm_ceil_ps and _mm_round_ps
|
||||
// functions. Rounds the elements of `op` according to `rounding`.
|
||||
"round.ps" => {
|
||||
let [op, rounding] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [op, rounding] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
round_all::<rustc_apfloat::ieee::Single>(this, op, rounding, dest)?;
|
||||
}
|
||||
@ -100,16 +95,14 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// functions. Rounds the first element of `right` according to `rounding`
|
||||
// and copies the remaining elements from `left`.
|
||||
"round.sd" => {
|
||||
let [left, right, rounding] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right, rounding] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
round_first::<rustc_apfloat::ieee::Double>(this, left, right, rounding, dest)?;
|
||||
}
|
||||
// Used to implement the _mm_floor_pd, _mm_ceil_pd and _mm_round_pd
|
||||
// functions. Rounds the elements of `op` according to `rounding`.
|
||||
"round.pd" => {
|
||||
let [op, rounding] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [op, rounding] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
round_all::<rustc_apfloat::ieee::Double>(this, op, rounding, dest)?;
|
||||
}
|
||||
@ -151,8 +144,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// offsets specified in `imm`.
|
||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mpsadbw_epu8
|
||||
"mpsadbw" => {
|
||||
let [left, right, imm] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
mpsadbw(this, left, right, imm, dest)?;
|
||||
}
|
||||
@ -161,8 +153,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Tests `(op & mask) == 0`, `(op & mask) == mask` or
|
||||
// `(op & mask) != 0 && (op & mask) != mask`
|
||||
"ptestz" | "ptestc" | "ptestnzc" => {
|
||||
let [op, mask] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [op, mask] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (all_zero, masked_set) = test_bits_masked(this, op, mask)?;
|
||||
let res = match unprefixed_name {
|
||||
|
@ -223,8 +223,7 @@ fn deconstruct_args<'tcx>(
|
||||
};
|
||||
|
||||
if is_explicit {
|
||||
let [str1, len1, str2, len2, imm] =
|
||||
ecx.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [str1, len1, str2, len2, imm] = ecx.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let imm = ecx.read_scalar(imm)?.to_u8()?;
|
||||
|
||||
let default_len = default_len::<u32>(imm);
|
||||
@ -237,8 +236,7 @@ fn deconstruct_args<'tcx>(
|
||||
|
||||
interp_ok((str1, str2, Some((len1, len2)), imm))
|
||||
} else {
|
||||
let [str1, str2, imm] =
|
||||
ecx.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [str1, str2, imm] = ecx.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let imm = ecx.read_scalar(imm)?.to_u8()?;
|
||||
|
||||
let array_layout = array_layout_fn(ecx, imm)?;
|
||||
@ -388,8 +386,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// search for a null terminator (see `deconstruct_args` for more details).
|
||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=924,925
|
||||
"pcmpistriz128" | "pcmpistris128" => {
|
||||
let [str1, str2, imm] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [str1, str2, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let imm = this.read_scalar(imm)?.to_u8()?;
|
||||
|
||||
let str = if unprefixed_name == "pcmpistris128" { str1 } else { str2 };
|
||||
@ -409,8 +406,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// than 16 for byte-sized operands or 8 for word-sized operands.
|
||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=1046,1047
|
||||
"pcmpestriz128" | "pcmpestris128" => {
|
||||
let [_, len1, _, len2, imm] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [_, len1, _, len2, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let len = if unprefixed_name == "pcmpestris128" { len1 } else { len2 };
|
||||
let len = this.read_scalar(len)?.to_i32()?;
|
||||
let imm = this.read_scalar(imm)?.to_u8()?;
|
||||
@ -437,8 +433,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
return interp_ok(EmulateItemResult::NotSupported);
|
||||
}
|
||||
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let left = this.read_scalar(left)?;
|
||||
let right = this.read_scalar(right)?;
|
||||
|
||||
|
@ -32,8 +32,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Shuffles bytes from `left` using `right` as pattern.
|
||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shuffle_epi8
|
||||
"pshuf.b.128" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (left, left_len) = this.project_to_simd(left)?;
|
||||
let (right, right_len) = this.project_to_simd(right)?;
|
||||
@ -62,8 +61,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// integer values in `left` and `right`.
|
||||
"phadd.w.128" | "phadd.sw.128" | "phadd.d.128" | "phsub.w.128" | "phsub.sw.128"
|
||||
| "phsub.d.128" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (which, saturating) = match unprefixed_name {
|
||||
"phadd.w.128" | "phadd.d.128" => (mir::BinOp::Add, false),
|
||||
@ -82,8 +80,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// produces the output at index `i`.
|
||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_maddubs_epi16
|
||||
"pmadd.ub.sw.128" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (left, left_len) = this.project_to_simd(left)?;
|
||||
let (right, right_len) = this.project_to_simd(right)?;
|
||||
@ -118,8 +115,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// 1 and then taking the bits `1..=16`.
|
||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mulhrs_epi16
|
||||
"pmul.hr.sw.128" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
pmulhrsw(this, left, right, dest)?;
|
||||
}
|
||||
@ -129,8 +125,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// is writen to the corresponding output element.
|
||||
// Basically, we multiply `left` with `right.signum()`.
|
||||
"psign.b.128" | "psign.w.128" | "psign.d.128" => {
|
||||
let [left, right] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
psign(this, left, right, dest)?;
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
//@ignore-target: windows # No libc socketpair on Windows
|
||||
//~^ERROR: deadlocked
|
||||
//~^^ERROR: deadlocked
|
||||
// test_race depends on a deterministic schedule.
|
||||
//@compile-flags: -Zmiri-preemption-rate=0
|
||||
//@error-in-other-file: deadlock
|
||||
|
||||
use std::thread;
|
||||
|
||||
// Test the behaviour of a thread being blocked on read, get unblocked, then blocked again.
|
||||
|
||||
// The expected execution is
|
||||
// 1. Thread 1 blocks.
|
||||
// 2. Thread 2 blocks.
|
||||
// 3. Thread 3 unblocks both thread 1 and thread 2.
|
||||
// 4. Thread 1 reads.
|
||||
// 5. Thread 2's `read` can never complete -> deadlocked.
|
||||
|
||||
fn main() {
|
||||
let mut fds = [-1, -1];
|
||||
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
|
||||
assert_eq!(res, 0);
|
||||
let thread1 = thread::spawn(move || {
|
||||
// Let this thread block on read.
|
||||
let mut buf: [u8; 3] = [0; 3];
|
||||
let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
|
||||
assert_eq!(res, 3);
|
||||
assert_eq!(&buf, "abc".as_bytes());
|
||||
});
|
||||
let thread2 = thread::spawn(move || {
|
||||
// Let this thread block on read.
|
||||
let mut buf: [u8; 3] = [0; 3];
|
||||
let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
|
||||
//~^ERROR: deadlocked
|
||||
assert_eq!(res, 3);
|
||||
assert_eq!(&buf, "abc".as_bytes());
|
||||
});
|
||||
let thread3 = thread::spawn(move || {
|
||||
// Unblock thread1 by writing something.
|
||||
let data = "abc".as_bytes().as_ptr();
|
||||
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
|
||||
assert_eq!(res, 3);
|
||||
});
|
||||
thread1.join().unwrap();
|
||||
thread2.join().unwrap();
|
||||
thread3.join().unwrap();
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
error: deadlock: the evaluated program deadlocked
|
||||
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
||||
|
|
||||
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
||||
| ^ the evaluated program deadlocked
|
||||
|
|
||||
= note: BACKTRACE:
|
||||
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
||||
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||
note: inside `main`
|
||||
--> tests/fail-dep/libc/socketpair_block_read_twice.rs:LL:CC
|
||||
|
|
||||
LL | thread2.join().unwrap();
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: deadlock: the evaluated program deadlocked
|
||||
|
|
||||
= note: the evaluated program deadlocked
|
||||
= note: (no span available)
|
||||
= note: BACKTRACE on thread `unnamed-ID`:
|
||||
|
||||
error: deadlock: the evaluated program deadlocked
|
||||
--> tests/fail-dep/libc/socketpair_block_read_twice.rs:LL:CC
|
||||
|
|
||||
LL | let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
|
||||
| ^ the evaluated program deadlocked
|
||||
|
|
||||
= note: BACKTRACE on thread `unnamed-ID`:
|
||||
= note: inside closure at tests/fail-dep/libc/socketpair_block_read_twice.rs:LL:CC
|
||||
|
||||
error: deadlock: the evaluated program deadlocked
|
||||
|
|
||||
= note: the evaluated program deadlocked
|
||||
= note: (no span available)
|
||||
= note: BACKTRACE on thread `unnamed-ID`:
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
@ -0,0 +1,49 @@
|
||||
//@ignore-target: windows # No libc socketpair on Windows
|
||||
//~^ERROR: deadlocked
|
||||
//~^^ERROR: deadlocked
|
||||
// test_race depends on a deterministic schedule.
|
||||
//@compile-flags: -Zmiri-preemption-rate=0
|
||||
//@error-in-other-file: deadlock
|
||||
|
||||
use std::thread;
|
||||
|
||||
// Test the behaviour of a thread being blocked on write, get unblocked, then blocked again.
|
||||
|
||||
// The expected execution is
|
||||
// 1. Thread 1 blocks.
|
||||
// 2. Thread 2 blocks.
|
||||
// 3. Thread 3 unblocks both thread 1 and thread 2.
|
||||
// 4. Thread 1 reads.
|
||||
// 5. Thread 2's `write` can never complete -> deadlocked.
|
||||
fn main() {
|
||||
let mut fds = [-1, -1];
|
||||
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
|
||||
assert_eq!(res, 0);
|
||||
let arr1: [u8; 212992] = [1; 212992];
|
||||
// Exhaust the space in the buffer so the subsequent write will block.
|
||||
let res = unsafe { libc::write(fds[0], arr1.as_ptr() as *const libc::c_void, 212992) };
|
||||
assert_eq!(res, 212992);
|
||||
let thread1 = thread::spawn(move || {
|
||||
let data = "abc".as_bytes().as_ptr();
|
||||
// The write below will be blocked because the buffer is already full.
|
||||
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
|
||||
assert_eq!(res, 3);
|
||||
});
|
||||
let thread2 = thread::spawn(move || {
|
||||
let data = "abc".as_bytes().as_ptr();
|
||||
// The write below will be blocked because the buffer is already full.
|
||||
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
|
||||
//~^ERROR: deadlocked
|
||||
assert_eq!(res, 3);
|
||||
});
|
||||
let thread3 = thread::spawn(move || {
|
||||
// Unblock thread1 by freeing up some space.
|
||||
let mut buf: [u8; 3] = [0; 3];
|
||||
let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
|
||||
assert_eq!(res, 3);
|
||||
assert_eq!(buf, [1, 1, 1]);
|
||||
});
|
||||
thread1.join().unwrap();
|
||||
thread2.join().unwrap();
|
||||
thread3.join().unwrap();
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
error: deadlock: the evaluated program deadlocked
|
||||
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
||||
|
|
||||
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
||||
| ^ the evaluated program deadlocked
|
||||
|
|
||||
= note: BACKTRACE:
|
||||
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
||||
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||
note: inside `main`
|
||||
--> tests/fail-dep/libc/socketpair_block_write_twice.rs:LL:CC
|
||||
|
|
||||
LL | thread2.join().unwrap();
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: deadlock: the evaluated program deadlocked
|
||||
|
|
||||
= note: the evaluated program deadlocked
|
||||
= note: (no span available)
|
||||
= note: BACKTRACE on thread `unnamed-ID`:
|
||||
|
||||
error: deadlock: the evaluated program deadlocked
|
||||
--> tests/fail-dep/libc/socketpair_block_write_twice.rs:LL:CC
|
||||
|
|
||||
LL | let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
|
||||
| ^ the evaluated program deadlocked
|
||||
|
|
||||
= note: BACKTRACE on thread `unnamed-ID`:
|
||||
= note: inside closure at tests/fail-dep/libc/socketpair_block_write_twice.rs:LL:CC
|
||||
|
||||
error: deadlock: the evaluated program deadlocked
|
||||
|
|
||||
= note: the evaluated program deadlocked
|
||||
= note: (no span available)
|
||||
= note: BACKTRACE on thread `unnamed-ID`:
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
@ -1,12 +0,0 @@
|
||||
//@ignore-target: windows # no libc socketpair on Windows
|
||||
|
||||
// This is temporarily here because blocking on fd is not supported yet.
|
||||
// When blocking is eventually supported, this will be moved to pass-dep/libc/libc-socketpair
|
||||
|
||||
fn main() {
|
||||
let mut fds = [-1, -1];
|
||||
let _ = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
|
||||
// The read below will be blocked because the buffer is empty.
|
||||
let mut buf: [u8; 3] = [0; 3];
|
||||
let _res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; //~ERROR: blocking isn't supported
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
error: unsupported operation: socketpair/pipe/pipe2 read: blocking isn't supported yet
|
||||
error: deadlock: the evaluated program deadlocked
|
||||
--> tests/fail-dep/libc/socketpair_read_blocking.rs:LL:CC
|
||||
|
|
||||
LL | let _res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ socketpair/pipe/pipe2 read: blocking isn't supported yet
|
||||
| ^ the evaluated program deadlocked
|
||||
|
|
||||
= help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
|
||||
= note: BACKTRACE:
|
||||
= note: inside `main` at tests/fail-dep/libc/socketpair_read_blocking.rs:LL:CC
|
||||
|
||||
|
@ -1,16 +0,0 @@
|
||||
//@ignore-target: windows # no libc socketpair on Windows
|
||||
// This is temporarily here because blocking on fd is not supported yet.
|
||||
// When blocking is eventually supported, this will be moved to pass-dep/libc/libc-socketpair
|
||||
fn main() {
|
||||
let mut fds = [-1, -1];
|
||||
let _ = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
|
||||
// Write size > buffer capacity
|
||||
// Used up all the space in the buffer.
|
||||
let arr1: [u8; 212992] = [1; 212992];
|
||||
let _ = unsafe { libc::write(fds[0], arr1.as_ptr() as *const libc::c_void, 212992) };
|
||||
let data = "abc".as_bytes().as_ptr();
|
||||
// The write below will be blocked as the buffer is full.
|
||||
let _ = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; //~ERROR: blocking isn't supported
|
||||
let mut buf: [u8; 3] = [0; 3];
|
||||
let _res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
error: unsupported operation: socketpair/pipe/pipe2 write: blocking isn't supported yet
|
||||
error: deadlock: the evaluated program deadlocked
|
||||
--> tests/fail-dep/libc/socketpair_write_blocking.rs:LL:CC
|
||||
|
|
||||
LL | let _ = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ socketpair/pipe/pipe2 write: blocking isn't supported yet
|
||||
| ^ the evaluated program deadlocked
|
||||
|
|
||||
= help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
|
||||
= note: BACKTRACE:
|
||||
= note: inside `main` at tests/fail-dep/libc/socketpair_write_blocking.rs:LL:CC
|
||||
|
||||
|
@ -0,0 +1,29 @@
|
||||
//@compile-flags: -Zmiri-tree-borrows -Zmiri-provenance-gc=0
|
||||
|
||||
// Shows the effect of the optimization of #4008.
|
||||
// The diagnostics change, but not the error itself.
|
||||
|
||||
// When this method is called, the tree will be a single line and look like this,
|
||||
// with other_ptr being the root at the top
|
||||
// other_ptr = root : Active
|
||||
// intermediary : Frozen // an intermediary node
|
||||
// m : Reserved
|
||||
fn write_to_mut(m: &mut u8, other_ptr: *const u8) {
|
||||
unsafe {
|
||||
std::hint::black_box(*other_ptr);
|
||||
}
|
||||
// In line 17 above, m should have become Reserved (protected) so that this write is impossible.
|
||||
// However, that does not happen because the read above is not forwarded to the subtree below
|
||||
// the Frozen intermediary node. This does not affect UB, however, because the Frozen that blocked
|
||||
// the read already prevents any child writes.
|
||||
*m = 42; //~ERROR: /write access through .* is forbidden/
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let root = 42u8;
|
||||
unsafe {
|
||||
let intermediary = &root;
|
||||
let data = &mut *(core::ptr::addr_of!(*intermediary) as *mut u8);
|
||||
write_to_mut(data, core::ptr::addr_of!(root));
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
error: Undefined Behavior: write access through <TAG> at ALLOC[0x0] is forbidden
|
||||
--> tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs:LL:CC
|
||||
|
|
||||
LL | *m = 42;
|
||||
| ^^^^^^^ write access through <TAG> at ALLOC[0x0] is forbidden
|
||||
|
|
||||
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
|
||||
= help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
|
||||
= help: the conflicting tag <TAG> has state Frozen which forbids this child write access
|
||||
help: the accessed tag <TAG> was created here
|
||||
--> tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs:LL:CC
|
||||
|
|
||||
LL | fn write_to_mut(m: &mut u8, other_ptr: *const u8) {
|
||||
| ^
|
||||
help: the conflicting tag <TAG> was created here, in the initial state Frozen
|
||||
--> tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs:LL:CC
|
||||
|
|
||||
LL | let intermediary = &root;
|
||||
| ^^^^^
|
||||
= note: BACKTRACE (of the first span):
|
||||
= note: inside `write_to_mut` at tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs:LL:CC
|
||||
note: inside `main`
|
||||
--> tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs:LL:CC
|
||||
|
|
||||
LL | write_to_mut(data, core::ptr::addr_of!(root));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
18
src/tools/miri/tests/native-lib/pass/ptr_read_access.stderr
Normal file
18
src/tools/miri/tests/native-lib/pass/ptr_read_access.stderr
Normal file
@ -0,0 +1,18 @@
|
||||
warning: sharing memory with a native function
|
||||
--> tests/native-lib/pass/ptr_read_access.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { print_pointer(&x) };
|
||||
| ^^^^^^^^^^^^^^^^^ sharing memory with a native function called via FFI
|
||||
|
|
||||
= help: when memory is shared with a native function call, Miri stops tracking initialization and provenance for that memory
|
||||
= help: in particular, Miri assumes that the native call initializes all memory it has access to
|
||||
= help: Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory
|
||||
= help: what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free
|
||||
= note: BACKTRACE:
|
||||
= note: inside `test_access_pointer` at tests/native-lib/pass/ptr_read_access.rs:LL:CC
|
||||
note: inside `main`
|
||||
--> tests/native-lib/pass/ptr_read_access.rs:LL:CC
|
||||
|
|
||||
LL | test_access_pointer();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
18
src/tools/miri/tests/native-lib/pass/ptr_write_access.stderr
Normal file
18
src/tools/miri/tests/native-lib/pass/ptr_write_access.stderr
Normal file
@ -0,0 +1,18 @@
|
||||
warning: sharing memory with a native function
|
||||
--> tests/native-lib/pass/ptr_write_access.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { increment_int(&mut x) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ sharing memory with a native function called via FFI
|
||||
|
|
||||
= help: when memory is shared with a native function call, Miri stops tracking initialization and provenance for that memory
|
||||
= help: in particular, Miri assumes that the native call initializes all memory it has access to
|
||||
= help: Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory
|
||||
= help: what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free
|
||||
= note: BACKTRACE:
|
||||
= note: inside `test_increment_int` at tests/native-lib/pass/ptr_write_access.rs:LL:CC
|
||||
note: inside `main`
|
||||
--> tests/native-lib/pass/ptr_write_access.rs:LL:CC
|
||||
|
|
||||
LL | test_increment_int();
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -10,6 +10,8 @@ fn main() {
|
||||
test_socketpair();
|
||||
test_socketpair_threaded();
|
||||
test_race();
|
||||
test_blocking_read();
|
||||
test_blocking_write();
|
||||
}
|
||||
|
||||
fn test_socketpair() {
|
||||
@ -136,3 +138,51 @@ fn test_race() {
|
||||
thread::yield_now();
|
||||
thread1.join().unwrap();
|
||||
}
|
||||
|
||||
// Test the behaviour of a socketpair getting blocked on read and subsequently unblocked.
|
||||
fn test_blocking_read() {
|
||||
let mut fds = [-1, -1];
|
||||
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
|
||||
assert_eq!(res, 0);
|
||||
let thread1 = thread::spawn(move || {
|
||||
// Let this thread block on read.
|
||||
let mut buf: [u8; 3] = [0; 3];
|
||||
let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
|
||||
assert_eq!(res, 3);
|
||||
assert_eq!(&buf, "abc".as_bytes());
|
||||
});
|
||||
let thread2 = thread::spawn(move || {
|
||||
// Unblock thread1 by doing writing something.
|
||||
let data = "abc".as_bytes().as_ptr();
|
||||
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
|
||||
assert_eq!(res, 3);
|
||||
});
|
||||
thread1.join().unwrap();
|
||||
thread2.join().unwrap();
|
||||
}
|
||||
|
||||
// Test the behaviour of a socketpair getting blocked on write and subsequently unblocked.
|
||||
fn test_blocking_write() {
|
||||
let mut fds = [-1, -1];
|
||||
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
|
||||
assert_eq!(res, 0);
|
||||
let arr1: [u8; 212992] = [1; 212992];
|
||||
// Exhaust the space in the buffer so the subsequent write will block.
|
||||
let res = unsafe { libc::write(fds[0], arr1.as_ptr() as *const libc::c_void, 212992) };
|
||||
assert_eq!(res, 212992);
|
||||
let thread1 = thread::spawn(move || {
|
||||
let data = "abc".as_bytes().as_ptr();
|
||||
// The write below will be blocked because the buffer is already full.
|
||||
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
|
||||
assert_eq!(res, 3);
|
||||
});
|
||||
let thread2 = thread::spawn(move || {
|
||||
// Unblock thread1 by freeing up some space.
|
||||
let mut buf: [u8; 3] = [0; 3];
|
||||
let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
|
||||
assert_eq!(res, 3);
|
||||
assert_eq!(buf, [1, 1, 1]);
|
||||
});
|
||||
thread1.join().unwrap();
|
||||
thread2.join().unwrap();
|
||||
}
|
||||
|
@ -5,7 +5,21 @@ use std::{env, mem, ptr};
|
||||
fn main() {
|
||||
test_clocks();
|
||||
test_posix_gettimeofday();
|
||||
test_localtime_r();
|
||||
test_localtime_r_gmt();
|
||||
test_localtime_r_pst();
|
||||
test_localtime_r_epoch();
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "freebsd",
|
||||
target_os = "android"
|
||||
))]
|
||||
test_localtime_r_multiple_calls_deduplication();
|
||||
// Architecture-specific tests.
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
test_localtime_r_future_32b();
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
test_localtime_r_future_64b();
|
||||
}
|
||||
|
||||
/// Tests whether clock support exists at all
|
||||
@ -46,14 +60,9 @@ fn test_posix_gettimeofday() {
|
||||
assert_eq!(is_error, -1);
|
||||
}
|
||||
|
||||
fn test_localtime_r() {
|
||||
// Set timezone to GMT.
|
||||
let key = "TZ";
|
||||
env::set_var(key, "GMT");
|
||||
|
||||
const TIME_SINCE_EPOCH: libc::time_t = 1712475836;
|
||||
let custom_time_ptr = &TIME_SINCE_EPOCH;
|
||||
let mut tm = libc::tm {
|
||||
/// Helper function to create an empty tm struct.
|
||||
fn create_empty_tm() -> libc::tm {
|
||||
libc::tm {
|
||||
tm_sec: 0,
|
||||
tm_min: 0,
|
||||
tm_hour: 0,
|
||||
@ -77,7 +86,17 @@ fn test_localtime_r() {
|
||||
target_os = "android"
|
||||
))]
|
||||
tm_zone: std::ptr::null_mut::<libc::c_char>(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Original GMT test
|
||||
fn test_localtime_r_gmt() {
|
||||
// Set timezone to GMT.
|
||||
let key = "TZ";
|
||||
env::set_var(key, "GMT");
|
||||
const TIME_SINCE_EPOCH: libc::time_t = 1712475836; // 2024-04-07 07:43:56 GMT
|
||||
let custom_time_ptr = &TIME_SINCE_EPOCH;
|
||||
let mut tm = create_empty_tm();
|
||||
let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) };
|
||||
|
||||
assert_eq!(tm.tm_sec, 56);
|
||||
@ -95,16 +114,12 @@ fn test_localtime_r() {
|
||||
target_os = "freebsd",
|
||||
target_os = "android"
|
||||
))]
|
||||
assert_eq!(tm.tm_gmtoff, 0);
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "freebsd",
|
||||
target_os = "android"
|
||||
))]
|
||||
unsafe {
|
||||
assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00")
|
||||
};
|
||||
{
|
||||
assert_eq!(tm.tm_gmtoff, 0);
|
||||
unsafe {
|
||||
assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00");
|
||||
}
|
||||
}
|
||||
|
||||
// The returned value is the pointer passed in.
|
||||
assert!(ptr::eq(res, &mut tm));
|
||||
@ -112,3 +127,191 @@ fn test_localtime_r() {
|
||||
// Remove timezone setting.
|
||||
env::remove_var(key);
|
||||
}
|
||||
|
||||
/// PST timezone test (testing different timezone handling).
|
||||
fn test_localtime_r_pst() {
|
||||
let key = "TZ";
|
||||
env::set_var(key, "PST8PDT");
|
||||
const TIME_SINCE_EPOCH: libc::time_t = 1712475836; // 2024-04-07 07:43:56 GMT
|
||||
let custom_time_ptr = &TIME_SINCE_EPOCH;
|
||||
let mut tm = create_empty_tm();
|
||||
|
||||
let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) };
|
||||
|
||||
assert_eq!(tm.tm_sec, 56);
|
||||
assert_eq!(tm.tm_min, 43);
|
||||
assert_eq!(tm.tm_hour, 0); // 7 - 7 = 0 (PDT offset)
|
||||
assert_eq!(tm.tm_mday, 7);
|
||||
assert_eq!(tm.tm_mon, 3);
|
||||
assert_eq!(tm.tm_year, 124);
|
||||
assert_eq!(tm.tm_wday, 0);
|
||||
assert_eq!(tm.tm_yday, 97);
|
||||
assert_eq!(tm.tm_isdst, -1); // DST information unavailable
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "freebsd",
|
||||
target_os = "android"
|
||||
))]
|
||||
{
|
||||
assert_eq!(tm.tm_gmtoff, -7 * 3600); // -7 hours in seconds
|
||||
unsafe {
|
||||
assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "-07");
|
||||
}
|
||||
}
|
||||
|
||||
assert!(ptr::eq(res, &mut tm));
|
||||
env::remove_var(key);
|
||||
}
|
||||
|
||||
/// Unix epoch test (edge case testing).
|
||||
fn test_localtime_r_epoch() {
|
||||
let key = "TZ";
|
||||
env::set_var(key, "GMT");
|
||||
const TIME_SINCE_EPOCH: libc::time_t = 0; // 1970-01-01 00:00:00
|
||||
let custom_time_ptr = &TIME_SINCE_EPOCH;
|
||||
let mut tm = create_empty_tm();
|
||||
|
||||
let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) };
|
||||
|
||||
assert_eq!(tm.tm_sec, 0);
|
||||
assert_eq!(tm.tm_min, 0);
|
||||
assert_eq!(tm.tm_hour, 0);
|
||||
assert_eq!(tm.tm_mday, 1);
|
||||
assert_eq!(tm.tm_mon, 0);
|
||||
assert_eq!(tm.tm_year, 70);
|
||||
assert_eq!(tm.tm_wday, 4); // Thursday
|
||||
assert_eq!(tm.tm_yday, 0);
|
||||
assert_eq!(tm.tm_isdst, -1);
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "freebsd",
|
||||
target_os = "android"
|
||||
))]
|
||||
{
|
||||
assert_eq!(tm.tm_gmtoff, 0);
|
||||
unsafe {
|
||||
assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00");
|
||||
}
|
||||
}
|
||||
|
||||
assert!(ptr::eq(res, &mut tm));
|
||||
env::remove_var(key);
|
||||
}
|
||||
|
||||
/// Future date test (testing large values).
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
fn test_localtime_r_future_64b() {
|
||||
let key = "TZ";
|
||||
env::set_var(key, "GMT");
|
||||
|
||||
// Using 2050-01-01 00:00:00 for 64-bit systems
|
||||
// value that's safe for 64-bit time_t
|
||||
const TIME_SINCE_EPOCH: libc::time_t = 2524608000;
|
||||
let custom_time_ptr = &TIME_SINCE_EPOCH;
|
||||
let mut tm = create_empty_tm();
|
||||
|
||||
let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) };
|
||||
|
||||
assert_eq!(tm.tm_sec, 0);
|
||||
assert_eq!(tm.tm_min, 0);
|
||||
assert_eq!(tm.tm_hour, 0);
|
||||
assert_eq!(tm.tm_mday, 1);
|
||||
assert_eq!(tm.tm_mon, 0);
|
||||
assert_eq!(tm.tm_year, 150); // 2050 - 1900
|
||||
assert_eq!(tm.tm_wday, 6); // Saturday
|
||||
assert_eq!(tm.tm_yday, 0);
|
||||
assert_eq!(tm.tm_isdst, -1);
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "freebsd",
|
||||
target_os = "android"
|
||||
))]
|
||||
{
|
||||
assert_eq!(tm.tm_gmtoff, 0);
|
||||
unsafe {
|
||||
assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00");
|
||||
}
|
||||
}
|
||||
|
||||
assert!(ptr::eq(res, &mut tm));
|
||||
env::remove_var(key);
|
||||
}
|
||||
|
||||
/// Future date test (testing large values for 32b target).
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
fn test_localtime_r_future_32b() {
|
||||
let key = "TZ";
|
||||
env::set_var(key, "GMT");
|
||||
|
||||
// Using 2030-01-01 00:00:00 for 32-bit systems
|
||||
// Safe value within i32 range
|
||||
const TIME_SINCE_EPOCH: libc::time_t = 1893456000;
|
||||
let custom_time_ptr = &TIME_SINCE_EPOCH;
|
||||
let mut tm = create_empty_tm();
|
||||
|
||||
let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) };
|
||||
|
||||
// Verify 2030-01-01 00:00:00
|
||||
assert_eq!(tm.tm_sec, 0);
|
||||
assert_eq!(tm.tm_min, 0);
|
||||
assert_eq!(tm.tm_hour, 0);
|
||||
assert_eq!(tm.tm_mday, 1);
|
||||
assert_eq!(tm.tm_mon, 0);
|
||||
assert_eq!(tm.tm_year, 130); // 2030 - 1900
|
||||
assert_eq!(tm.tm_wday, 2); // Tuesday
|
||||
assert_eq!(tm.tm_yday, 0);
|
||||
assert_eq!(tm.tm_isdst, -1);
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "freebsd",
|
||||
target_os = "android"
|
||||
))]
|
||||
{
|
||||
assert_eq!(tm.tm_gmtoff, 0);
|
||||
unsafe {
|
||||
assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00");
|
||||
}
|
||||
}
|
||||
|
||||
assert!(ptr::eq(res, &mut tm));
|
||||
env::remove_var(key);
|
||||
}
|
||||
|
||||
/// Tests the behavior of `localtime_r` with multiple calls to ensure deduplication of `tm_zone` pointers.
|
||||
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd", target_os = "android"))]
|
||||
fn test_localtime_r_multiple_calls_deduplication() {
|
||||
let key = "TZ";
|
||||
env::set_var(key, "PST8PDT");
|
||||
|
||||
const TIME_SINCE_EPOCH_BASE: libc::time_t = 1712475836; // Base timestamp: 2024-04-07 07:43:56 GMT
|
||||
const NUM_CALLS: usize = 50;
|
||||
|
||||
let mut unique_pointers = std::collections::HashSet::new();
|
||||
|
||||
for i in 0..NUM_CALLS {
|
||||
let timestamp = TIME_SINCE_EPOCH_BASE + (i as libc::time_t * 3600); // Increment by 1 hour for each call
|
||||
let mut tm: libc::tm = create_empty_tm();
|
||||
let tm_ptr = unsafe { libc::localtime_r(×tamp, &mut tm) };
|
||||
|
||||
assert!(!tm_ptr.is_null(), "localtime_r failed for timestamp {timestamp}");
|
||||
|
||||
unique_pointers.insert(tm.tm_zone);
|
||||
}
|
||||
|
||||
let unique_count = unique_pointers.len();
|
||||
|
||||
assert!(
|
||||
unique_count >= 2 && unique_count <= (NUM_CALLS - 1),
|
||||
"Unexpected number of unique tm_zone pointers: {} (expected between 2 and {})",
|
||||
unique_count,
|
||||
NUM_CALLS - 1
|
||||
);
|
||||
}
|
||||
|
@ -27,11 +27,8 @@ fn main() {
|
||||
test_file_sync();
|
||||
test_errors();
|
||||
test_rename();
|
||||
// solarish needs to support readdir/readdir64 for these tests.
|
||||
if cfg!(not(any(target_os = "solaris", target_os = "illumos"))) {
|
||||
test_directory();
|
||||
test_canonicalize();
|
||||
}
|
||||
test_directory();
|
||||
test_canonicalize();
|
||||
test_from_raw_os_error();
|
||||
#[cfg(unix)]
|
||||
test_pread_pwrite();
|
||||
@ -279,7 +276,12 @@ fn test_directory() {
|
||||
.collect::<BTreeMap<_, _>>()
|
||||
);
|
||||
// Deleting the directory should fail, since it is not empty.
|
||||
assert_eq!(ErrorKind::DirectoryNotEmpty, remove_dir(&dir_path).unwrap_err().kind());
|
||||
|
||||
// Solaris/Illumos `rmdir` call set errno to EEXIST if directory contains
|
||||
// other entries than `.` and `..`.
|
||||
// https://docs.oracle.com/cd/E86824_01/html/E54765/rmdir-2.html
|
||||
let err = remove_dir(&dir_path).unwrap_err().kind();
|
||||
assert!(matches!(err, ErrorKind::AlreadyExists | ErrorKind::DirectoryNotEmpty));
|
||||
// Clean up the files in the directory
|
||||
remove_file(&path_1).unwrap();
|
||||
remove_file(&path_2).unwrap();
|
||||
|
@ -29,3 +29,6 @@ review_labels = ["S-waiting-on-review"]
|
||||
remove_labels = ["S-waiting-on-author"]
|
||||
# Those labels are added when PR author requests a review from an assignee
|
||||
add_labels = ["S-waiting-on-review"]
|
||||
|
||||
# Automatically close and reopen PRs made by bots to run CI on them
|
||||
[bot-pull-requests]
|
||||
|
Loading…
Reference in New Issue
Block a user