psm-0.1.21/.cargo_vcs_info.json0000644000000001410000000000100117100ustar { "git": { "sha1": "071bab374dbe4e7f6fd397573372865258f0438f" }, "path_in_vcs": "psm" }psm-0.1.21/.gitignore000064400000000000000000000000621046102023000124720ustar 00000000000000/target **/*.rs.bk /target **/*.rs.bk Cargo.lock psm-0.1.21/Cargo.lock0000644000000005510000000000100076700ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "cc" version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" [[package]] name = "psm" version = "0.1.21" dependencies = [ "cc", ] psm-0.1.21/Cargo.toml0000644000000016330000000000100077150ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] name = "psm" version = "0.1.21" authors = ["Simonas Kazlauskas "] build = "build.rs" description = "Portable Stack Manipulation: stack manipulation and introspection routines" documentation = "https://docs.rs/psm/0.1.20" readme = "README.mkd" keywords = [ "stack", "no_std", ] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/stacker/" [dependencies] [build-dependencies.cc] version = "1.0.2" psm-0.1.21/Cargo.toml.orig000064400000000000000000000006651046102023000134020ustar 00000000000000[package] name = "psm" version = "0.1.21" authors = ["Simonas Kazlauskas "] build = "build.rs" description = "Portable Stack Manipulation: stack manipulation and introspection routines" keywords = ["stack", "no_std"] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/stacker/" documentation = "https://docs.rs/psm/0.1.20" readme = "README.mkd" [dependencies] [build-dependencies] cc = "1.0.2" psm-0.1.21/LICENSE-APACHE000064400000000000000000000247161046102023000124420ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. psm-0.1.21/LICENSE-MIT000064400000000000000000000020461046102023000121420ustar 00000000000000Copyright © 2018, Simonas Kazlauskas Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. psm-0.1.21/README.mkd000064400000000000000000000231611046102023000121410ustar 00000000000000# Portable Stack Manipulation This crate provides very portable functions to control the stack pointer and inspect the properties of the stack. This crate does not attempt to provide safe abstractions to any operations, the only goals are correctness, portability and efficiency (in that exact order). As a consequence most functions you’ll see in this crate are unsafe. Unless you’re writing a safe abstraction over stack manipulation, this is not the crate you want. Instead consider one of the safe abstractions over this crate. A good place to look at is the crates.io’s reverse dependency list. # Platform support The following table lists supported targets and architectures with notes on the level of current support and knowledge about the target. The three columns “Available”, “Tested” and “Callstack” imply an increasingly high level of support. * “Available” basically means that the code builds and the assembly files have been written for the target; * “Tested” means that the assembly code has been tested or otherwise verified to be correct. For most targets it also means that continuous integration is set up; * “Callstack” means that the assembly code has been written with due care to support unwinding the stack and displaying the call frames (i.e. `gdb backtrace` works as expected).
Target Support
Architecture OS Available Tested Callstack
Notes
x86_64 apple-ios Yes Yes Yes
Target has been tested locally.
windows Yes Yes Yes, but disabled
Stacks allocated the usual way are not valid to be used on Windows and the functions to allocate a stack in a proper way is a Windows implementation detail. As a (unnecessarily slow and inflexible) alternative use [Fibers][fibers].
* Yes Yes Yes
i686
i586
i386
apple-ios Yes Unknown Unknown
linux-android Unknown Unknown Unknown
The assembly files are available, but the target hasn’t been verified to build
windows No No No
The code technically works on my local machine, but exception handling does not correctly work on appveyor, which makes me unwilling to mark this as working yet. Stacks allocated the usual way are not valid to be used on Windows and the functions to allocate a stack in a proper way is a Windows implementation detail. As a (unnecessarily slow and inflexible) alternative use [Fibers][fibers].
* Yes Yes Yes
aarch64 apple-ios Yes Unknown Yes
aarch64-apple-ios has not been tested. iOS hardware is necessary to run these tests.
fuchsia
unknown-cloudabi
Unknown Unknown Yes
windows No No No
Stacks allocated the usual way are not valid to be used on Windows and the functions to allocate a stack in a proper way is a Windows implementation detail. As a (unnecessarily slow and inflexible) alternative use [Fibers][fibers].
* Yes Yes Yes
arm
armv7
apple-ios Yes Unknown Unknown
armv7-apple-ios has not been tested. iOS hardware is necessary to run these tests.
windows No No No
Stacks allocated the usual way are not valid to be used on Windows and the functions to allocate a stack in a proper way is a Windows implementation detail. As a (unnecessarily slow and inflexible) alternative use [Fibers][fibers].
* Yes Yes Yes
armv5te * Unknown Unknown Unknown
thumbv6
thumbv7
* Unknown Unknown Unknown
mips
mipsel
linux Yes Yes Yes
Only the o32 ABI is supported and will be used for all 32-bit MIPS targets.
mips64
mips64el
linux Yes Yes Yes
powerpc linux Yes Yes Yes
Callstack generation may fail at certain well defined ranges of the program, although the usual compiler-generated code fails at similar points itself.
powerpc64 linux Yes Yes Yes
Callstack generation may fail at certain well defined ranges of the program, although the usual compiler-generated code fails at similar points itself.
AIX Yes Yes Yes
powerpc64le linux Yes Yes Yes
Callstack generation may fail at certain well defined ranges of the program, although the usual compiler-generated code fails at similar points itself.
s390x linux Yes Locally Yes
Test runner on CI hangs, local verification has been done on a qemu-system-s390x VM. It may be possible to add CI testing in the future via qemu’s full-system emulation.
sparc linux Unknown Unknown Unknown
A Rust target for 32-bit SPARC exists, but no Linux distributions actually have a 32-bit SPARC distribution, so verification is infeasible. The actual assembly code has been written conservatively, modelled after the 64-bit SPARC code. and so has a non-zero chance of working.
sparc64 linux Yes Locally Yes
Has been manually verified to work on the [GCC Farm Project] machines. It may be possible to add CI testing in the future via qemu’s full-system emulation.
sparc9 solaris Yes Unknown Unknown
Uses the same assembly as the sparc64-linux-gnu target. This target has no rustc builds and therefore the correct operation of this target could not be verified at the moment.
wasm * No No No
This library is not applicable to the target. WASM hasn’t a specified C ABI, the callstack is not even in an address space and does not appear to be manipulatable.
asmjs * No No No
Feasibility/necessity hasn’t been acertained.
nvptx * No No No
Feasibility/necessity hasn’t been acertained.
msp430 * No No No
Haven’t gotten to it yet...
riscv32 * Yes No Unknown
Although the assembly code has not been tested, it is a straightforward copy of the 64-bit version. Unless there is a non-obvious mistake, this should work fine.
riscv64 * Yes Locally Unknown
The assembly code for riscv64 has been tested locally with a C caller.
loongarch64 * Yes Locally Unknown
The assembly code for loongarch64 has been tested locally with a C caller.
[GCC Farm Project]: https://cfarm.tetaneutral.net/ [fibers]: https://docs.microsoft.com/en-gb/windows/desktop/ProcThread/fibers # License PSM is licensed under either of * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) at your option. psm-0.1.21/build.rs000064400000000000000000000106221046102023000121520ustar 00000000000000extern crate cc; fn find_assembly( arch: &str, endian: &str, os: &str, env: &str, masm: bool, ) -> Option<(&'static str, bool)> { match (arch, endian, os, env) { // The implementations for stack switching exist, but, officially, doing so without Fibers // is not supported in Windows. For x86_64 the implementation actually works locally, // but failed tests in CI (???). Might want to have a feature for experimental support // here. ("x86", _, "windows", _) => { if masm { Some(("src/arch/x86_msvc.asm", false)) } else { Some(("src/arch/x86_windows_gnu.s", false)) } } ("x86_64", _, "windows", _) => { if masm { Some(("src/arch/x86_64_msvc.asm", false)) } else { Some(("src/arch/x86_64_windows_gnu.s", false)) } } ("arm", _, "windows", "msvc") => Some(("src/arch/arm_armasm.asm", false)), ("aarch64", _, "windows", _) => { if masm { Some(("src/arch/aarch64_armasm.asm", false)) } else { Some(("src/arch/aarch_aapcs64.s", false)) } } ("x86", _, _, _) => Some(("src/arch/x86.s", true)), ("x86_64", _, _, _) => Some(("src/arch/x86_64.s", true)), ("arm", _, _, _) => Some(("src/arch/arm_aapcs.s", true)), ("aarch64", _, _, _) => Some(("src/arch/aarch_aapcs64.s", true)), ("powerpc", _, _, _) => Some(("src/arch/powerpc32.s", true)), ("powerpc64", _, _, "musl") => Some(("src/arch/powerpc64_openpower.s", true)), ("powerpc64", "little", _, _) => Some(("src/arch/powerpc64_openpower.s", true)), ("powerpc64", _, "aix", _) => Some(("src/arch/powerpc64_aix.s", true)), ("powerpc64", _, _, _) => Some(("src/arch/powerpc64.s", true)), ("s390x", _, _, _) => Some(("src/arch/zseries_linux.s", true)), ("mips", _, _, _) => Some(("src/arch/mips_eabi.s", true)), ("mips64", _, _, _) => Some(("src/arch/mips64_eabi.s", true)), ("sparc64", _, _, _) => Some(("src/arch/sparc64.s", true)), ("sparc", _, _, _) => Some(("src/arch/sparc_sysv.s", true)), ("riscv32", _, _, _) => Some(("src/arch/riscv.s", true)), ("riscv64", _, _, _) => Some(("src/arch/riscv64.s", true)), ("wasm32", _, _, _) => Some(("src/arch/wasm32.o", true)), ("loongarch64", _, _, _) => Some(("src/arch/loongarch64.s", true)), _ => None, } } fn main() { use std::env::var; let arch = var("CARGO_CFG_TARGET_ARCH").unwrap(); let env = var("CARGO_CFG_TARGET_ENV").unwrap(); let os = var("CARGO_CFG_TARGET_OS").unwrap(); let endian = var("CARGO_CFG_TARGET_ENDIAN").unwrap(); let mut cfg = cc::Build::new(); let msvc = cfg.get_compiler().is_like_msvc(); // If we're targeting msvc, either via regular MS toolchain or clang-cl, we // will _usually_ want to use the regular Microsoft assembler if it exists, // which is done for us within cc, however it _probably_ won't exist if // we're in a cross-compilation context pm a platform that can't natively // run Windows executables, so in that case we instead use the the equivalent // GAS assembly file instead. This logic can be removed once LLVM natively // supports compiling MASM, but that is not stable yet let masm = msvc && var("HOST").expect("HOST env not set").contains("windows"); let asm = if let Some((asm, canswitch)) = find_assembly(&arch, &endian, &os, &env, masm) { println!("cargo:rustc-cfg=asm"); if canswitch { println!("cargo:rustc-cfg=switchable_stack") } asm } else { println!( "cargo:warning=Target {}-{}-{} has no assembly files!", arch, os, env ); return; }; if !msvc { cfg.flag("-xassembler-with-cpp"); cfg.define(&*format!("CFG_TARGET_OS_{}", os), None); cfg.define(&*format!("CFG_TARGET_ARCH_{}", arch), None); cfg.define(&*format!("CFG_TARGET_ENV_{}", env), None); } // For wasm targets we ship a precompiled `*.o` file so we just pass that // directly to `ar` to assemble an archive. Otherwise we're actually // compiling the source assembly file. if asm.ends_with(".o") { cfg.object(asm); } else { cfg.file(asm); } cfg.compile("libpsm_s.a"); } psm-0.1.21/examples/info.rs000064400000000000000000000006101046102023000136200ustar 00000000000000extern crate psm; psm::psm_stack_information! { yes { fn main() { println!("Stack is {:?} and is at {:p} currently", psm::StackDirection::new(), psm::stack_pointer()); } } no { fn main() { eprintln!("Stack information not supported by this target"); } } } #[test] fn run_example() { main(); } psm-0.1.21/examples/on_stack_fibo.rs000064400000000000000000000052031046102023000154700ustar 00000000000000extern crate psm; psm::psm_stack_manipulation! { yes { use std::alloc; #[inline(never)] fn fib(n: usize, stack_limit: *mut u8) -> Option { // match psm::StackDirection::new() { // psm::StackDirection::Ascending => if psm::stack_pointer() > stack_limit { // return None; // } // psm::StackDirection::Descending => if psm::stack_pointer() < stack_limit { // return None; // } // } match n { 0 => Some(0), 1 => Some(1), _ => fib(n - 1, stack_limit).and_then(|x| fib(n - 2, stack_limit).map(|y| x + y)), } } const STACK_ALIGN: usize = 4096; const STACK_REDLINE: usize = 512; const FIB_COUNTS: [(usize, u64); 3] = [ (8, 21), (16, 987), (32, 2178309), ]; fn main() { let mut stack_size = 1024 * 128; unsafe { for &(n, expected) in FIB_COUNTS.iter() { loop { println!("fib({}) with {} bytes of stack", n, stack_size - STACK_REDLINE); let layout = alloc::Layout::from_size_align(stack_size, STACK_ALIGN).unwrap(); let new_stack = alloc::alloc(layout); assert!(!new_stack.is_null(), "allocations must succeed!"); let max_stack = match psm::StackDirection::new() { psm::StackDirection::Ascending => new_stack.offset((stack_size - STACK_REDLINE) as isize), psm::StackDirection::Descending => new_stack.offset(STACK_REDLINE as isize), }; let result = psm::on_stack(new_stack, stack_size, || { fib(n, max_stack) }); alloc::dealloc(new_stack, layout); if let Some(res) = result { assert_eq!(res, expected); println!("fib({}) = {}", n, res); break; } else { println!("Stack not large enough!"); stack_size *= 2; } } } } } } no { fn main() { eprintln!("Stack manipulation not supported by this target"); } } } #[test] fn run_example() { main() } psm-0.1.21/examples/on_stack_fibo_alloc_each_frame.rs000064400000000000000000000025561046102023000210040ustar 00000000000000extern crate psm; psm::psm_stack_manipulation! { yes { use std::alloc; const STACK_ALIGN: usize = 4096; const FRAME_SIZE: usize = 4096; const FIB_COUNTS: [(usize, u64); 3] = [ (8, 21), (16, 987), (24, 46368), ]; #[inline(never)] fn fib(n: usize) -> u64 { unsafe { let layout = alloc::Layout::from_size_align(FRAME_SIZE, STACK_ALIGN).unwrap(); let new_stack = alloc::alloc(layout); assert!(!new_stack.is_null(), "allocations must succeed!"); let r = match n { 0 => 0, 1 => 1, _ => { psm::on_stack(new_stack, FRAME_SIZE, || { fib(n - 1) + fib(n - 2) }) } }; alloc::dealloc(new_stack, layout); r } } fn main() { for &(n, expected) in FIB_COUNTS.iter() { let res = fib(n); assert_eq!(res, expected); println!("fib({}) = {}", n, res); } } } no { fn main() { eprintln!("Stack manipulation not supported by this target"); } } } #[test] fn run_example() { main() } psm-0.1.21/examples/panics.rs000064400000000000000000000031041046102023000141430ustar 00000000000000extern crate psm; use std::panic; const CHAIN_DEPTH: usize = 16; psm::psm_stack_manipulation! { yes { use std::alloc; const STACK_ALIGN: usize = 4096; // Generating backraces (because of RUST_BACKTRACE) create a few quite large frames, so it is // important, that all frames have sufficient amount of available memory to not run over the // stack... const FRAME_SIZE: usize = 4096 * 10; fn panic_chain(depth: usize) { if depth == 0 { panic!("full chain!"); } else { unsafe { let layout = alloc::Layout::from_size_align(FRAME_SIZE, STACK_ALIGN).unwrap(); let new_stack = alloc::alloc(layout); assert!(!new_stack.is_null(), "allocations must succeed!"); let p = psm::on_stack(new_stack, FRAME_SIZE, || { panic::catch_unwind(|| { panic_chain(depth - 1); }) }); alloc::dealloc(new_stack, layout); p.map_err(panic::resume_unwind).unwrap() } } } fn main() { panic_chain(CHAIN_DEPTH); } #[test] fn run_example() { assert!(panic::catch_unwind(|| { panic_chain(CHAIN_DEPTH); }).is_err(), "Panic did not propagate!"); } } no { fn main() { eprintln!("Stack manipulation not supported by this target"); } } } psm-0.1.21/examples/replace_stack_1.rs000064400000000000000000000017761046102023000157230ustar 00000000000000extern crate psm; psm::psm_stack_manipulation! { yes { use std::alloc; const STACK_SIZE: usize = 4096 * 64; const STACK_ALIGN: usize = 4096; fn main() { println!("current stack pointer is {:p}", psm::stack_pointer()); unsafe { let new_stack = alloc::alloc(alloc::Layout::from_size_align(STACK_SIZE, STACK_ALIGN).unwrap()); println!("new stack is between {:p} and {:p}", new_stack, new_stack.offset(STACK_SIZE as isize)); psm::replace_stack(new_stack, STACK_SIZE, || { println!("after replacement stack pointer is {:p}", psm::stack_pointer()); ::std::process::exit(0); }); } } } no { fn main() { eprintln!("Stack manipulation not supported by this target"); } } } #[test] fn run_example() { // NOTE: intentionally out-of-processes, as the example exits with `process::exit(0)`. main() } psm-0.1.21/examples/thread.rs000064400000000000000000000032041046102023000141360ustar 00000000000000extern crate psm; psm::psm_stack_manipulation! { yes { use std::alloc; const STACK_ALIGN: usize = 4096; const FRAME_SIZE: usize = 4096; const FIB_COUNTS: [(usize, u64); 3] = [ (8, 21), (16, 987), (24, 46368), ]; #[inline(never)] fn fib(n: usize) -> u64 { unsafe { let layout = alloc::Layout::from_size_align(FRAME_SIZE, STACK_ALIGN).unwrap(); let new_stack = alloc::alloc(layout); assert!(!new_stack.is_null(), "allocations must succeed!"); let r = match n { 0 => 0, 1 => 1, _ => { psm::on_stack(new_stack, FRAME_SIZE, || { fib(n - 1) + fib(n - 2) }) } }; alloc::dealloc(new_stack, layout); r } } fn main() { for (n, expected, handle) in FIB_COUNTS.iter().map(|&(n, expected)| (n, expected, std::thread::spawn(move || { fib(n) })) ) { if let Ok(res) = handle.join() { assert_eq!(res, expected); println!("fib({}) = {}", n, res); } else { panic!("joining a thread returned an Err"); } } } } no { fn main() { eprintln!("Stack manipulation not supported by this target"); } } } #[test] fn run_example() { main() } psm-0.1.21/src/arch/aarch64_armasm.asm000064400000000000000000000011161046102023000155010ustar 00000000000000 AREA |.text|, CODE, READONLY GLOBAL |rust_psm_stack_direction| ALIGN 4 |rust_psm_stack_direction| PROC orr w0, wzr, #2 ret ENDP GLOBAL |rust_psm_stack_pointer| ALIGN 4 |rust_psm_stack_pointer| PROC mov x0, sp ret ENDP GLOBAL |rust_psm_replace_stack| ALIGN 4 |rust_psm_replace_stack| PROC mov sp, x2 br x1 ENDP GLOBAL |rust_psm_on_stack| ALIGN 4 |rust_psm_on_stack| PROC stp x29, x30, [sp, #-16]! mov x29, sp mov sp, x3 blr x2 mov sp, x29 ldp x29, x30, [sp], #16 ret ENDP END psm-0.1.21/src/arch/aarch_aapcs64.s000064400000000000000000000041311046102023000147720ustar 00000000000000#include "psm.h" .text #if CFG_TARGET_OS_darwin || CFG_TARGET_OS_macos || CFG_TARGET_OS_ios #define GLOBL(fnname) .globl _##fnname #define TYPE(fnname) #define FUNCTION(fnname) _##fnname #define SIZE(fnname,endlabel) #elif CFG_TARGET_OS_windows #define GLOBL(fnname) .globl fnname #define TYPE(fnname) #define FUNCTION(fnname) fnname #define SIZE(fnname,endlabel) #else #define GLOBL(fnname) .globl fnname #define TYPE(fnname) .type fnname,@function #define FUNCTION(fnname) fnname #define SIZE(fnname,endlabel) .size fnname,endlabel-fnname #endif GLOBL(rust_psm_stack_direction) .p2align 2 TYPE(rust_psm_stack_direction) FUNCTION(rust_psm_stack_direction): /* extern "C" fn() -> u8 */ .cfi_startproc orr w0, wzr, #STACK_DIRECTION_DESCENDING ret .rust_psm_stack_direction_end: SIZE(rust_psm_stack_direction,.rust_psm_stack_direction_end) .cfi_endproc GLOBL(rust_psm_stack_pointer) .p2align 2 TYPE(rust_psm_stack_pointer) FUNCTION(rust_psm_stack_pointer): /* extern "C" fn() -> *mut u8 */ .cfi_startproc mov x0, sp ret .rust_psm_stack_pointer_end: SIZE(rust_psm_stack_pointer,.rust_psm_stack_pointer_end) .cfi_endproc GLOBL(rust_psm_replace_stack) .p2align 2 TYPE(rust_psm_replace_stack) FUNCTION(rust_psm_replace_stack): /* extern "C" fn(r0: usize, r1: extern "C" fn(usize), r2: *mut u8) */ .cfi_startproc /* All we gotta do is set the stack pointer to %rdx & tail-call the callback in %rsi */ mov sp, x2 br x1 .rust_psm_replace_stack_end: SIZE(rust_psm_replace_stack,.rust_psm_replace_stack_end) .cfi_endproc GLOBL(rust_psm_on_stack) .p2align 2 TYPE(rust_psm_on_stack) FUNCTION(rust_psm_on_stack): /* extern "C" fn(r0: usize, r1: usize, r2: extern "C" fn(usize, usize), r3: *mut u8) */ .cfi_startproc stp x29, x30, [sp, #-16]! .cfi_def_cfa sp, 16 mov x29, sp .cfi_def_cfa x29, 16 .cfi_offset x29, -16 .cfi_offset x30, -8 mov sp, x3 blr x2 mov sp, x29 .cfi_def_cfa sp, 16 ldp x29, x30, [sp], #16 .cfi_def_cfa sp, 0 .cfi_restore x29 .cfi_restore x30 ret .rust_psm_on_stack_end: SIZE(rust_psm_on_stack,.rust_psm_on_stack_end) .cfi_endproc psm-0.1.21/src/arch/arm_aapcs.s000064400000000000000000000043731046102023000143310ustar 00000000000000#include "psm.h" .text .syntax unified #if CFG_TARGET_OS_darwin || CFG_TARGET_OS_macos || CFG_TARGET_OS_ios #define GLOBL(fnname) .globl _##fnname #define THUMBTYPE(fnname) .thumb_func _##fnname #define FUNCTION(fnname) _##fnname #define THUMBFN .code 16 #define SIZE(fnname,endlabel) #define FNSTART #define CANTUNWIND #define FNEND #else #define GLOBL(fnname) .globl fnname #define THUMBTYPE(fnname) .type fnname,%function #define FUNCTION(fnname) fnname #define THUMBFN .code 16 #define SIZE(fnname,endlabel) .size fnname,endlabel-fnname #define FNSTART .fnstart #define CANTUNWIND .cantunwind #define FNEND .fnend #endif GLOBL(rust_psm_stack_direction) .p2align 2 THUMBTYPE(rust_psm_stack_direction) THUMBFN FUNCTION(rust_psm_stack_direction): /* extern "C" fn() -> u8 */ FNSTART .cfi_startproc /* movs to support Thumb-1 */ movs r0, #STACK_DIRECTION_DESCENDING bx lr .rust_psm_stack_direction_end: SIZE(rust_psm_stack_direction,.rust_psm_stack_direction_end) .cfi_endproc CANTUNWIND FNEND GLOBL(rust_psm_stack_pointer) .p2align 2 THUMBTYPE(rust_psm_stack_pointer) THUMBFN FUNCTION(rust_psm_stack_pointer): /* extern "C" fn() -> *mut u8 */ FNSTART .cfi_startproc mov r0, sp bx lr .rust_psm_stack_pointer_end: SIZE(rust_psm_stack_pointer,.rust_psm_stack_pointer_end) .cfi_endproc CANTUNWIND FNEND GLOBL(rust_psm_replace_stack) .p2align 2 THUMBTYPE(rust_psm_replace_stack) THUMBFN FUNCTION(rust_psm_replace_stack): /* extern "C" fn(r0: usize, r1: extern "C" fn(usize), r2: *mut u8) */ FNSTART .cfi_startproc /* All we gotta do is set the stack pointer to %rdx & tail-call the callback in %rsi */ mov sp, r2 bx r1 .rust_psm_replace_stack_end: SIZE(rust_psm_replace_stack,.rust_psm_replace_stack_end) .cfi_endproc CANTUNWIND FNEND GLOBL(rust_psm_on_stack) .p2align 2 THUMBTYPE(rust_psm_on_stack) THUMBFN FUNCTION(rust_psm_on_stack): /* extern "C" fn(r0: usize, r1: usize, r2: extern "C" fn(usize, usize), r3: *mut u8) */ FNSTART .cfi_startproc push {r4, lr} .cfi_def_cfa_offset 8 mov r4, sp .cfi_def_cfa_register r4 .cfi_offset lr, -4 .cfi_offset r4, -8 mov sp, r3 blx r2 mov sp, r4 .cfi_restore sp pop {r4, pc} .rust_psm_on_stack_end: SIZE(rust_psm_on_stack,.rust_psm_on_stack_end) .cfi_endproc CANTUNWIND FNEND psm-0.1.21/src/arch/arm_armasm.asm000064400000000000000000000010711046102023000150300ustar 00000000000000 THUMB AREA |.text|, CODE, READONLY GLOBAL |rust_psm_stack_direction| ALIGN 4 |rust_psm_stack_direction| PROC movs r0, #2 bx lr ENDP GLOBAL |rust_psm_stack_pointer| ALIGN 4 |rust_psm_stack_pointer| PROC mov r0, sp bx lr ENDP GLOBAL |rust_psm_replace_stack| ALIGN 4 |rust_psm_replace_stack| PROC mov sp, r2 bx r1 ENDP GLOBAL |rust_psm_on_stack| ALIGN 4 |rust_psm_on_stack| PROC push {r4, lr} mov r4, sp mov sp, r3 blx r2 mov sp, r4 pop {r4, pc} ENDP END psm-0.1.21/src/arch/loongarch64.s000064400000000000000000000030401046102023000145170ustar 00000000000000#include "psm.h" .text .globl rust_psm_stack_direction .align 2 .type rust_psm_stack_direction,@function rust_psm_stack_direction: /* extern "C" fn() -> u8 */ .cfi_startproc li.w $r4, STACK_DIRECTION_DESCENDING jr $r1 .rust_psm_stack_direction_end: .size rust_psm_stack_direction,.rust_psm_stack_direction_end-rust_psm_stack_direction .cfi_endproc .globl rust_psm_stack_pointer .align 2 .type rust_psm_stack_pointer,@function rust_psm_stack_pointer: /* extern "C" fn() -> *mut u8 */ .cfi_startproc move $r4, $r3 jr $r1 .rust_psm_stack_pointer_end: .size rust_psm_stack_pointer,.rust_psm_stack_pointer_end-rust_psm_stack_pointer .cfi_endproc .globl rust_psm_replace_stack .align 2 .type rust_psm_replace_stack,@function rust_psm_replace_stack: /* extern "C" fn(r4: usize, r5: extern "C" fn(usize), r6: *mut u8) */ .cfi_startproc move $r3, $r6 jr $r5 .rust_psm_replace_stack_end: .size rust_psm_replace_stack,.rust_psm_replace_stack_end-rust_psm_replace_stack .cfi_endproc .globl rust_psm_on_stack .align 2 .type rust_psm_on_stack,@function rust_psm_on_stack: /* extern "C" fn(r4: usize, r5: usize, r6: extern "C" fn(usize, usize), r7: *mut u8) */ .cfi_startproc st.d $r1, $r7, -8 st.d $r3, $r7, -16 addi.d $r3, $r7, -16 .cfi_def_cfa 3, 16 .cfi_offset 1, -8 .cfi_offset 3, -16 jirl $r1, $r6, 0 ld.d $r1, $r3, 8 .cfi_restore 1 ld.d $r3, $r3, 0 .cfi_restore 3 jr $r1 .rust_psm_on_stack_end: .size rust_psm_on_stack,.rust_psm_on_stack_end-rust_psm_on_stack .cfi_endproc psm-0.1.21/src/arch/mips64_eabi.s000064400000000000000000000041421046102023000144770ustar 00000000000000/* Not only MIPS has 20 different ABIs... nobody tells anybody what specific variant of which ABI is used where. This is an "EABI" implementation based on the following page: http://www.cygwin.com/ml/binutils/2003-06/msg00436.html */ #include "psm.h" .set noreorder /* we’ll manage the delay slots on our own, thanks! */ .text .globl rust_psm_stack_direction .p2align 3 .type rust_psm_stack_direction,@function .ent rust_psm_stack_direction /* extern "C" fn() -> u8 */ rust_psm_stack_direction: .cfi_startproc jr $31 addiu $2, $zero, STACK_DIRECTION_DESCENDING .end rust_psm_stack_direction .rust_psm_stack_direction_end: .size rust_psm_stack_direction,.rust_psm_stack_direction_end-rust_psm_stack_direction .cfi_endproc .globl rust_psm_stack_pointer .p2align 3 .type rust_psm_stack_pointer,@function .ent rust_psm_stack_pointer /* extern "C" fn() -> *mut u8 */ rust_psm_stack_pointer: .cfi_startproc jr $31 move $2, $29 .end rust_psm_stack_pointer .rust_psm_stack_pointer_end: .size rust_psm_stack_pointer,.rust_psm_stack_pointer_end-rust_psm_stack_pointer .cfi_endproc .globl rust_psm_replace_stack .p2align 3 .type rust_psm_replace_stack,@function .ent rust_psm_replace_stack /* extern "C" fn(r4: usize, r5: extern "C" fn(usize), r6: *mut u8) */ rust_psm_replace_stack: .cfi_startproc move $25, $5 jr $5 move $29, $6 .end rust_psm_replace_stack .rust_psm_replace_stack_end: .size rust_psm_replace_stack,.rust_psm_on_stack_end-rust_psm_on_stack .cfi_endproc .globl rust_psm_on_stack .p2align 3 .type rust_psm_on_stack,@function .ent rust_psm_on_stack /* extern "C" fn(r4: usize, r5: usize, r6: extern "C" fn(usize), r7: *mut u8) */ rust_psm_on_stack: .cfi_startproc sd $29, -8($7) sd $31, -16($7) .cfi_def_cfa 7, 0 .cfi_offset 31, -16 .cfi_offset 29, -8 move $25, $6 jalr $31, $6 daddiu $29, $7, -16 .cfi_def_cfa 29, 16 ld $31, 0($29) .cfi_restore 31 ld $29, 8($29) .cfi_restore 29 jr $31 nop .end rust_psm_on_stack .rust_psm_on_stack_end: .size rust_psm_on_stack,.rust_psm_on_stack_end-rust_psm_on_stack .cfi_endproc psm-0.1.21/src/arch/mips_eabi.s000064400000000000000000000041471046102023000143320ustar 00000000000000/* Not only MIPS has 20 different ABIs... nobody tells anybody what specific variant of which ABI is used where. This is an "EABI" implementation based on the following page: http://www.cygwin.com/ml/binutils/2003-06/msg00436.html */ #include "psm.h" .set noreorder /* we’ll manage the delay slots on our own, thanks! */ .text .abicalls .globl rust_psm_stack_direction .p2align 2 .type rust_psm_stack_direction,@function .ent rust_psm_stack_direction /* extern "C" fn() -> u8 */ rust_psm_stack_direction: .cfi_startproc jr $31 addiu $2, $zero, STACK_DIRECTION_DESCENDING .end rust_psm_stack_direction .rust_psm_stack_direction_end: .size rust_psm_stack_direction,.rust_psm_stack_direction_end-rust_psm_stack_direction .cfi_endproc .globl rust_psm_stack_pointer .p2align 2 .type rust_psm_stack_pointer,@function .ent rust_psm_stack_pointer /* extern "C" fn() -> *mut u8 */ rust_psm_stack_pointer: .cfi_startproc jr $31 move $2, $29 .end rust_psm_stack_pointer .rust_psm_stack_pointer_end: .size rust_psm_stack_pointer,.rust_psm_stack_pointer_end-rust_psm_stack_pointer .cfi_endproc .globl rust_psm_replace_stack .p2align 2 .type rust_psm_replace_stack,@function .ent rust_psm_replace_stack /* extern "C" fn(r4: usize, r5: extern "C" fn(usize), r6: *mut u8) */ rust_psm_replace_stack: .cfi_startproc move $25, $5 jr $5 move $29, $6 .end rust_psm_replace_stack .rust_psm_replace_stack_end: .size rust_psm_replace_stack,.rust_psm_on_stack_end-rust_psm_on_stack .cfi_endproc .globl rust_psm_on_stack .p2align 2 .type rust_psm_on_stack,@function .ent rust_psm_on_stack /* extern "C" fn(r4: usize, r5: usize, r6: extern "C" fn(usize), r7: *mut u8) */ rust_psm_on_stack: .cfi_startproc sw $29, -4($7) sw $31, -8($7) .cfi_def_cfa 7, 0 .cfi_offset 31, -8 .cfi_offset 29, -4 move $25, $6 jalr $31, $6 addiu $29, $7, -8 .cfi_def_cfa 29, 8 lw $31, 0($29) .cfi_restore 31 lw $29, 4($29) .cfi_restore 29 jr $31 nop .end rust_psm_on_stack .rust_psm_on_stack_end: .size rust_psm_on_stack,.rust_psm_on_stack_end-rust_psm_on_stack .cfi_endproc psm-0.1.21/src/arch/powerpc32.s000064400000000000000000000036531046102023000142270ustar 00000000000000#include "psm.h" /* FIXME: this probably does not cover all ABIs? Tested with sysv only, possibly works for AIX as well? */ .text .globl rust_psm_stack_direction .p2align 2 .type rust_psm_stack_direction,@function rust_psm_stack_direction: /* extern "C" fn() -> u8 */ .cfi_startproc li 3, STACK_DIRECTION_DESCENDING blr .rust_psm_stack_direction_end: .size rust_psm_stack_direction,.rust_psm_stack_direction_end-rust_psm_stack_direction .cfi_endproc .globl rust_psm_stack_pointer .p2align 2 .type rust_psm_stack_pointer,@function rust_psm_stack_pointer: /* extern "C" fn() -> *mut u8 */ .cfi_startproc mr 3, 1 blr .rust_psm_stack_pointer_end: .size rust_psm_stack_pointer,.rust_psm_stack_pointer_end-rust_psm_stack_pointer .cfi_endproc .globl rust_psm_replace_stack .p2align 2 .type rust_psm_replace_stack,@function rust_psm_replace_stack: /* extern "C" fn(3: usize, 4: extern "C" fn(usize), 5: *mut u8) */ .cfi_startproc /* NOTE: perhaps add a debug-assertion for stack alignment? */ addi 5, 5, -16 mr 1, 5 mtctr 4 bctr .rust_psm_replace_stack_end: .size rust_psm_replace_stack,.rust_psm_replace_stack_end-rust_psm_replace_stack .cfi_endproc .globl rust_psm_on_stack .p2align 2 .type rust_psm_on_stack,@function rust_psm_on_stack: /* extern "C" fn(3: usize, 4: usize, 5: extern "C" fn(usize, usize), 6: *mut u8) */ .cfi_startproc mflr 0 stw 0, -24(6) sub 6, 6, 1 addi 6, 6, -32 stwux 1, 1, 6 .cfi_def_cfa r1, 32 .cfi_offset r1, -32 .cfi_offset lr, -24 mtctr 5 bctrl lwz 0, 8(1) mtlr 0 .cfi_restore lr /* FIXME: after this instruction backtrace breaks until control returns to the caller That being said compiler-generated code has the same issue, so I guess that is fine for now? */ lwz 1, 0(1) .cfi_restore r1 blr .rust_psm_on_stack_end: .size rust_psm_on_stack,.rust_psm_on_stack_end-rust_psm_on_stack .cfi_endproc psm-0.1.21/src/arch/powerpc64.s000064400000000000000000000047551046102023000142400ustar 00000000000000/* Implementation of the AIX-like PowerPC ABI. Seems to be used by the big-endian PowerPC targets. The following references were used during the implementation of this code: https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/com.ibm.aix.alangref/idalangref_rntime_stack.htm https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/com.ibm.aix.alangref/idalangref_reg_use_conv.htm https://www.ibm.com/developerworks/library/l-powasm4/index.html */ #include "psm.h" .text .globl rust_psm_stack_direction .p2align 2 .type rust_psm_stack_direction,@function rust_psm_stack_direction: /* extern "C" fn() -> u8 */ .cfi_startproc li 3, STACK_DIRECTION_DESCENDING blr .rust_psm_stack_direction_end: .size rust_psm_stack_direction,.rust_psm_stack_direction_end-rust_psm_stack_direction .cfi_endproc .globl rust_psm_stack_pointer .p2align 2 .type rust_psm_stack_pointer,@function rust_psm_stack_pointer: /* extern "C" fn() -> *mut u8 */ .cfi_startproc mr 3, 1 blr .rust_psm_stack_pointer_end: .size rust_psm_stack_pointer,.rust_psm_stack_pointer_end-rust_psm_stack_pointer .cfi_endproc .globl rust_psm_replace_stack .p2align 2 .type rust_psm_replace_stack,@function rust_psm_replace_stack: /* extern "C" fn(3: usize, 4: extern "C" fn(usize), 5: *mut u8) */ .cfi_startproc ld 2, 8(4) ld 4, 0(4) /* do not allocate the whole 112-byte sized frame, we know wont be used */ addi 5, 5, -48 mr 1, 5 mtctr 4 bctr .rust_psm_replace_stack_end: .size rust_psm_replace_stack,.rust_psm_replace_stack_end-rust_psm_replace_stack .cfi_endproc .globl rust_psm_on_stack .p2align 2 .type rust_psm_on_stack,@function rust_psm_on_stack: /* extern "C" fn(3: usize, 4: usize, 5: extern "C" fn(usize, usize), 6: *mut u8) */ .cfi_startproc mflr 0 std 2, -72(6) std 0, -8(6) sub 6, 6, 1 addi 6, 6, -112 stdux 1, 1, 6 .cfi_def_cfa r1, 112 .cfi_offset r1, -112 .cfi_offset r2, -72 .cfi_offset lr, -8 /* load the function pointer from TOC and make the call */ ld 2, 8(5) ld 5, 0(5) mtctr 5 bctrl ld 2, 40(1) .cfi_restore r2 ld 0, 104(1) mtlr 0 .cfi_restore lr /* FIXME: after this instruction backtrace breaks until control returns to the caller. That being said compiler-generated code has the same issue, so I guess that is fine for now? */ ld 1, 0(1) .cfi_restore r1 blr .rust_psm_on_stack_end: .size rust_psm_on_stack,.rust_psm_on_stack_end-rust_psm_on_stack .cfi_endproc psm-0.1.21/src/arch/powerpc64_aix.s000064400000000000000000000070311046102023000150670ustar 00000000000000.csect .text[PR],2 .file "powerpc64_aix.s" .globl rust_psm_stack_direction[DS] .globl .rust_psm_stack_direction .align 4 .csect rust_psm_stack_direction[DS],3 .vbyte 8, .rust_psm_stack_direction .vbyte 8, TOC[TC0] .vbyte 8, 0 .csect .text[PR],2 .rust_psm_stack_direction: # extern "C" fn() -> u8 li 3, 2 blr L..rust_psm_stack_direction_end: # Following bytes form the traceback table on AIX. # For specification, see https://www.ibm.com/docs/en/aix/7.2?topic=processor-traceback-tables. # For implementation, see https://github.com/llvm/llvm-project/blob/main/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp, # `PPCAIXAsmPrinter::emitTracebackTable`. .vbyte 4, 0x00000000 # Traceback table begin, for unwinder to search the table. .byte 0x00 # Version = 0 .byte 0x09 # Language = CPlusPlus, since rust is using C++-like LSDA. .byte 0x20 # -IsGlobaLinkage, -IsOutOfLineEpilogOrPrologue # +HasTraceBackTableOffset, -IsInternalProcedure # -HasControlledStorage, -IsTOCless # -IsFloatingPointPresent # -IsFloatingPointOperationLogOrAbortEnabled .byte 0x40 # -IsInterruptHandler, +IsFunctionNamePresent, -IsAllocaUsed # OnConditionDirective = 0, -IsCRSaved, -IsLRSaved .byte 0x80 # +IsBackChainStored, -IsFixup, NumOfFPRsSaved = 0 .byte 0x00 # -HasExtensionTable, -HasVectorInfo, NumOfGPRsSaved = 0 .byte 0x00 # NumberOfFixedParms = 0 .byte 0x01 # NumberOfFPParms = 0, +HasParmsOnStack .vbyte 4, L..rust_psm_stack_direction_end-.rust_psm_stack_direction #Function size .vbyte 2, 0x0018 # Function name len = 24 .byte "rust_psm_stack_direction" # Function Name .globl rust_psm_stack_pointer[DS] .globl .rust_psm_stack_pointer .align 4 .csect rust_psm_stack_pointer[DS],3 .vbyte 8, .rust_psm_stack_pointer .vbyte 8, TOC[TC0] .vbyte 8, 0 .csect .text[PR],2 .rust_psm_stack_pointer: # extern "C" fn() -> *mut u8 mr 3, 1 blr L..rust_psm_stack_pointer_end: .vbyte 4, 0x00000000 .byte 0x00 .byte 0x09 .byte 0x20 .byte 0x40 .byte 0x80 .byte 0x00 .byte 0x00 .byte 0x01 .vbyte 4, L..rust_psm_stack_pointer_end-.rust_psm_stack_pointer .vbyte 2, 0x0016 .byte "rust_psm_stack_pointer" .globl rust_psm_replace_stack[DS] .globl .rust_psm_replace_stack .align 4 .csect rust_psm_replace_stack[DS],3 .vbyte 8, .rust_psm_replace_stack .vbyte 8, TOC[TC0] .vbyte 8, 0 .csect .text[PR],2 .rust_psm_replace_stack: # extern "C" fn(3: usize, 4: extern "C" fn(usize), 5: *mut u8) # Load the function pointer and toc pointer from TOC and make the call. ld 2, 8(4) ld 4, 0(4) addi 5, 5, -48 mr 1, 5 mtctr 4 bctr L..rust_psm_replace_stack_end: .vbyte 4, 0x00000000 .byte 0x00 .byte 0x09 .byte 0x20 .byte 0x40 .byte 0x80 .byte 0x00 .byte 0x03 .byte 0x01 .vbyte 4, 0x00000000 # Parameter type = i, i, i .vbyte 4, L..rust_psm_replace_stack_end-.rust_psm_replace_stack .vbyte 2, 0x0016 .byte "rust_psm_replace_stack" .globl rust_psm_on_stack[DS] .globl .rust_psm_on_stack .align 4 .csect rust_psm_on_stack[DS],3 .vbyte 8, .rust_psm_on_stack .vbyte 8, TOC[TC0] .vbyte 8, 0 .csect .text[PR],2 .rust_psm_on_stack: # extern "C" fn(3: usize, 4: usize, 5: extern "C" fn(usize, usize), 6: *mut u8) mflr 0 std 2, -72(6) std 0, -8(6) sub 6, 6, 1 addi 6, 6, -112 stdux 1, 1, 6 ld 2, 8(5) ld 5, 0(5) mtctr 5 bctrl ld 2, 40(1) ld 0, 104(1) mtlr 0 ld 1, 0(1) blr L..rust_psm_on_stack_end: .vbyte 4, 0x00000000 .byte 0x00 .byte 0x09 .byte 0x20 .byte 0x41 .byte 0x80 .byte 0x00 .byte 0x04 .byte 0x01 .vbyte 4, 0x00000000 # Parameter type = i, i, i, i .vbyte 4, L..rust_psm_on_stack_end-.rust_psm_on_stack .vbyte 2, 0x0011 .byte "rust_psm_on_stack" .toc psm-0.1.21/src/arch/powerpc64_openpower.s000064400000000000000000000037751046102023000163370ustar 00000000000000/* Implementation of stack swtiching routines for OpenPOWER 64-bit ELF ABI The specification can be found at http://openpowerfoundation.org/wp-content/uploads/resources/leabi/content/ch_preface.html This ABI is usually used by the ppc64le targets. */ #include "psm.h" .text .abiversion 2 .globl rust_psm_stack_direction .p2align 4 .type rust_psm_stack_direction,@function rust_psm_stack_direction: /* extern "C" fn() -> u8 */ .cfi_startproc li 3, STACK_DIRECTION_DESCENDING blr .rust_psm_stack_direction_end: .size rust_psm_stack_direction,.rust_psm_stack_direction_end-rust_psm_stack_direction .cfi_endproc .globl rust_psm_stack_pointer .p2align 4 .type rust_psm_stack_pointer,@function rust_psm_stack_pointer: /* extern "C" fn() -> *mut u8 */ .cfi_startproc mr 3, 1 blr .rust_psm_stack_pointer_end: .size rust_psm_stack_pointer,.rust_psm_stack_pointer_end-rust_psm_stack_pointer .cfi_endproc .globl rust_psm_replace_stack .p2align 4 .type rust_psm_replace_stack,@function rust_psm_replace_stack: /* extern "C" fn(3: usize, 4: extern "C" fn(usize), 5: *mut u8) */ .cfi_startproc addi 5, 5, -32 mtctr 4 mr 12, 4 mr 1, 5 bctr .rust_psm_replace_stack_end: .size rust_psm_replace_stack,.rust_psm_replace_stack_end-rust_psm_replace_stack .cfi_endproc .globl rust_psm_on_stack .p2align 4 .type rust_psm_on_stack,@function rust_psm_on_stack: /* extern "C" fn(3: usize, 4: usize, 5: extern "C" fn(usize, usize), 6: *mut u8) */ .cfi_startproc mflr 0 std 0, -8(6) std 2, -24(6) sub 6, 6, 1 addi 6, 6, -48 stdux 1, 1, 6 .cfi_def_cfa r1, 48 .cfi_offset r1, -48 .cfi_offset r2, -24 .cfi_offset lr, -8 mr 12, 5 mtctr 5 bctrl ld 2, 24(1) .cfi_restore r2 ld 0, 40(1) mtlr 0 .cfi_restore lr /* FIXME: after this instructin backtrace breaks until control returns to the caller */ ld 1, 0(1) blr .rust_psm_on_stack_end: .size rust_psm_on_stack,.rust_psm_on_stack_end-rust_psm_on_stack .cfi_endproc psm-0.1.21/src/arch/psm.h000064400000000000000000000003401046102023000131550ustar 00000000000000#define STACK_DIRECTION_ASCENDING 1 #define STACK_DIRECTION_DESCENDING 2 /* Various defines for values produced by `-DCFG_TARGET_*` flags. Only needs to be mutually unique */ #define darwin 1 #define macos 2 #define ios 3 psm-0.1.21/src/arch/riscv.s000064400000000000000000000030431046102023000135220ustar 00000000000000#include "psm.h" .text .globl rust_psm_stack_direction .p2align 2 .type rust_psm_stack_direction,@function rust_psm_stack_direction: /* extern "C" fn() -> u8 */ .cfi_startproc li x10, STACK_DIRECTION_DESCENDING jr x1 .rust_psm_stack_direction_end: .size rust_psm_stack_direction,.rust_psm_stack_direction_end-rust_psm_stack_direction .cfi_endproc .globl rust_psm_stack_pointer .p2align 2 .type rust_psm_stack_pointer,@function rust_psm_stack_pointer: /* extern "C" fn() -> *mut u8 */ .cfi_startproc add x10, x2, x0 jr x1 .rust_psm_stack_pointer_end: .size rust_psm_stack_pointer,.rust_psm_stack_pointer_end-rust_psm_stack_pointer .cfi_endproc .globl rust_psm_replace_stack .p2align 2 .type rust_psm_replace_stack,@function rust_psm_replace_stack: /* extern "C" fn(x10: usize, x11: extern "C" fn(usize), x12: *mut u8) */ .cfi_startproc add x2, x12, x0 jr x11 .rust_psm_replace_stack_end: .size rust_psm_replace_stack,.rust_psm_replace_stack_end-rust_psm_replace_stack .cfi_endproc .globl rust_psm_on_stack .p2align 2 .type rust_psm_on_stack,@function rust_psm_on_stack: /* extern "C" fn(x10: usize, x11: usize, x12: extern "C" fn(usize, usize), x13: *mut u8) */ .cfi_startproc sw x1, -12(x13) sw x2, -16(x13) addi x2, x13, -16 .cfi_def_cfa x2, 16 .cfi_offset x1, -12 .cfi_offset x2, -16 jalr x1, x12, 0 lw x1, 4(x2) .cfi_restore x1 lw x2, 0(x2) .cfi_restore x2 jr x1 .rust_psm_on_stack_end: .size rust_psm_on_stack,.rust_psm_on_stack_end-rust_psm_on_stack .cfi_endproc psm-0.1.21/src/arch/riscv64.s000064400000000000000000000030411046102023000136720ustar 00000000000000#include "psm.h" .text .globl rust_psm_stack_direction .p2align 2 .type rust_psm_stack_direction,@function rust_psm_stack_direction: /* extern "C" fn() -> u8 */ .cfi_startproc li x10, STACK_DIRECTION_DESCENDING jr x1 .rust_psm_stack_direction_end: .size rust_psm_stack_direction,.rust_psm_stack_direction_end-rust_psm_stack_direction .cfi_endproc .globl rust_psm_stack_pointer .p2align 2 .type rust_psm_stack_pointer,@function rust_psm_stack_pointer: /* extern "C" fn() -> *mut u8 */ .cfi_startproc add x10, x2, x0 jr x1 .rust_psm_stack_pointer_end: .size rust_psm_stack_pointer,.rust_psm_stack_pointer_end-rust_psm_stack_pointer .cfi_endproc .globl rust_psm_replace_stack .p2align 2 .type rust_psm_replace_stack,@function rust_psm_replace_stack: /* extern "C" fn(x10: usize, x11: extern "C" fn(usize), x12: *mut u8) */ .cfi_startproc add x2, x12, x0 jr x11 .rust_psm_replace_stack_end: .size rust_psm_replace_stack,.rust_psm_replace_stack_end-rust_psm_replace_stack .cfi_endproc .globl rust_psm_on_stack .p2align 2 .type rust_psm_on_stack,@function rust_psm_on_stack: /* extern "C" fn(x10: usize, x11: usize, x12: extern "C" fn(usize, usize), x13: *mut u8) */ .cfi_startproc sd x1, -8(x13) sd x2, -16(x13) addi x2, x13, -16 .cfi_def_cfa x2, 16 .cfi_offset x1, -8 .cfi_offset x2, -16 jalr x1, x12, 0 ld x1, 8(x2) .cfi_restore x1 ld x2, 0(x2) .cfi_restore x2 jr x1 .rust_psm_on_stack_end: .size rust_psm_on_stack,.rust_psm_on_stack_end-rust_psm_on_stack .cfi_endproc psm-0.1.21/src/arch/sparc64.s000064400000000000000000000036221046102023000136610ustar 00000000000000#include "psm.h" .text .globl rust_psm_stack_direction .p2align 2 .type rust_psm_stack_direction,@function rust_psm_stack_direction: /* extern "C" fn() -> u8 */ .cfi_startproc jmpl %o7 + 8, %g0 mov STACK_DIRECTION_DESCENDING, %o0 .rust_psm_stack_direction_end: .size rust_psm_stack_direction,.rust_psm_stack_direction_end-rust_psm_stack_direction .cfi_endproc .globl rust_psm_stack_pointer .p2align 2 .type rust_psm_stack_pointer,@function rust_psm_stack_pointer: /* extern "C" fn() -> *mut u8 */ .cfi_startproc jmpl %o7 + 8, %g0 mov %o6, %o0 .rust_psm_stack_pointer_end: .size rust_psm_stack_pointer,.rust_psm_stack_pointer_end-rust_psm_stack_pointer .cfi_endproc .globl rust_psm_replace_stack .p2align 2 .type rust_psm_replace_stack,@function rust_psm_replace_stack: /* extern "C" fn(%i0: usize, %i1: extern "C" fn(usize), %i2: *mut u8) */ .cfi_startproc .cfi_def_cfa 0, 0 .cfi_return_column 0 jmpl %o1, %g0 /* WEIRD: Why is the LSB set for the %sp and %fp on SPARC?? */ add %o2, -0x7ff, %o6 .rust_psm_replace_stack_end: .size rust_psm_replace_stack,.rust_psm_replace_stack_end-rust_psm_replace_stack .cfi_endproc .globl rust_psm_on_stack .p2align 2 .type rust_psm_on_stack,@function rust_psm_on_stack: /* extern "C" fn(%i0: usize, %i1: usize, %i2: extern "C" fn(usize, usize), %i3: *mut u8) */ .cfi_startproc /* The fact that locals and saved register windows are offset by 2kB is very nasty property of SPARC architecture and ABI. In this case it forces us to slice off 2kB of the stack space outright for no good reason other than adapting to a botched design. */ save %o3, -0x87f, %o6 .cfi_def_cfa_register %fp .cfi_window_save .cfi_register %r15, %r31 mov %i1, %o1 jmpl %i2, %o7 mov %i0, %o0 ret restore .rust_psm_on_stack_end: .size rust_psm_on_stack,.rust_psm_on_stack_end-rust_psm_on_stack .cfi_endproc psm-0.1.21/src/arch/sparc_sysv.s000064400000000000000000000032721046102023000145740ustar 00000000000000#include "psm.h" /* FIXME: this ABI has definitely not been verified at all */ .text .globl rust_psm_stack_direction .p2align 2 .type rust_psm_stack_direction,@function rust_psm_stack_direction: /* extern "C" fn() -> u8 */ .cfi_startproc jmpl %o7 + 8, %g0 mov STACK_DIRECTION_DESCENDING, %o0 .rust_psm_stack_direction_end: .size rust_psm_stack_direction,.rust_psm_stack_direction_end-rust_psm_stack_direction .cfi_endproc .globl rust_psm_stack_pointer .p2align 2 .type rust_psm_stack_pointer,@function rust_psm_stack_pointer: /* extern "C" fn() -> *mut u8 */ .cfi_startproc jmpl %o7 + 8, %g0 mov %o6, %o0 .rust_psm_stack_pointer_end: .size rust_psm_stack_pointer,.rust_psm_stack_pointer_end-rust_psm_stack_pointer .cfi_endproc .globl rust_psm_replace_stack .p2align 2 .type rust_psm_replace_stack,@function rust_psm_replace_stack: /* extern "C" fn(%i0: usize, %i1: extern "C" fn(usize), %i2: *mut u8) */ .cfi_startproc .cfi_def_cfa 0, 0 .cfi_return_column 0 jmpl %o1, %g0 /* WEIRD: Why is the LSB set for the %sp and %fp on SPARC?? */ add %o2, -0x3ff, %o6 .rust_psm_replace_stack_end: .size rust_psm_replace_stack,.rust_psm_replace_stack_end-rust_psm_replace_stack .cfi_endproc .globl rust_psm_on_stack .p2align 2 .type rust_psm_on_stack,@function rust_psm_on_stack: /* extern "C" fn(%i0: usize, %i1: usize, %i2: extern "C" fn(usize, usize), %i3: *mut u8) */ .cfi_startproc save %o3, -0x43f, %o6 .cfi_def_cfa_register %fp .cfi_window_save .cfi_register %r15, %r31 mov %i1, %o1 jmpl %i2, %o7 mov %i0, %o0 ret restore .rust_psm_on_stack_end: .size rust_psm_on_stack,.rust_psm_on_stack_end-rust_psm_on_stack .cfi_endproc psm-0.1.21/src/arch/wasm32.o000064400000000000000000000006051046102023000135050ustar 00000000000000asm`````рenv__linear_memoryenv__indirect_function_tablepenv__stack_pointer ΀A # ' #!$   $  $  linking退rust_psm_stack_directionrust_psm_stack_pointerrust_psm_on_stackrust_psm_replace_stack reloc.CODE (1<Fpsm-0.1.21/src/arch/wasm32.s000064400000000000000000000032361046102023000135140ustar 00000000000000#include "psm.h" # Note that this function is not compiled when this package is uploaded to # crates.io, this source is only here as a reference for how the corresponding # wasm32.o was generated. This file can be compiled with: # # cpp psm/src/arch/wasm32.s | llvm-mc -o psm/src/arch/wasm32.o --arch=wasm32 -filetype=obj # # where you'll want to ensure that `llvm-mc` is from a relatively recent # version of LLVM. .globaltype __stack_pointer, i32 .globl rust_psm_stack_direction .type rust_psm_stack_direction,@function rust_psm_stack_direction: .functype rust_psm_stack_direction () -> (i32) i32.const STACK_DIRECTION_DESCENDING end_function .globl rust_psm_stack_pointer .type rust_psm_stack_pointer,@function rust_psm_stack_pointer: .functype rust_psm_stack_pointer () -> (i32) global.get __stack_pointer end_function .globl rust_psm_on_stack .type rust_psm_on_stack,@function rust_psm_on_stack: .functype rust_psm_on_stack (i32, i32, i32, i32) -> () # get our new stack argument, then save the old stack # pointer into that local local.get 3 global.get __stack_pointer local.set 3 global.set __stack_pointer # Call our indirect function specified local.get 0 local.get 1 local.get 2 call_indirect (i32, i32) -> () # restore the stack pointer before returning local.get 3 global.set __stack_pointer end_function .globl rust_psm_replace_stack .type rust_psm_replace_stack,@function rust_psm_replace_stack: .functype rust_psm_replace_stack (i32, i32, i32) -> () local.get 2 global.set __stack_pointer local.get 0 local.get 1 call_indirect (i32) -> () unreachable end_function psm-0.1.21/src/arch/x86.s000064400000000000000000000047751046102023000130360ustar 00000000000000#include "psm.h" /* NOTE: fastcall calling convention used on all x86 targets */ .text #if CFG_TARGET_OS_darwin || CFG_TARGET_OS_macos || CFG_TARGET_OS_ios #define GLOBL(fnname) .globl _##fnname #define TYPE(fnname) #define FUNCTION(fnname) _##fnname #define SIZE(fnname,endlabel) #else #define GLOBL(fnname) .globl fnname #define TYPE(fnname) .type fnname,@function #define FUNCTION(fnname) fnname #define SIZE(fnname,endlabel) .size fnname,endlabel-fnname #endif GLOBL(rust_psm_stack_direction) .p2align 4 TYPE(rust_psm_stack_direction) FUNCTION(rust_psm_stack_direction): /* extern "fastcall" fn() -> u8 (%al) */ .cfi_startproc movb $STACK_DIRECTION_DESCENDING, %al # always descending on x86_64 retl .rust_psm_stack_direction_end: SIZE(rust_psm_stack_direction,.rust_psm_stack_direction_end) .cfi_endproc GLOBL(rust_psm_stack_pointer) .p2align 4 TYPE(rust_psm_stack_pointer) FUNCTION(rust_psm_stack_pointer): /* extern "fastcall" fn() -> *mut u8 (%rax) */ .cfi_startproc leal 4(%esp), %eax retl .rust_psm_stack_pointer_end: SIZE(rust_psm_stack_pointer,.rust_psm_stack_pointer_end) .cfi_endproc GLOBL(rust_psm_replace_stack) .p2align 4 TYPE(rust_psm_replace_stack) FUNCTION(rust_psm_replace_stack): /* extern "fastcall" fn(%ecx: usize, %edx: extern "fastcall" fn(usize), 4(%esp): *mut u8) */ .cfi_startproc /* All we gotta do is set the stack pointer to 4(%esp) & tail-call the callback in %edx Note, that the callee expects the stack to be offset by 4 bytes (normally, a return address would be store there) off the required stack alignment on entry. To offset the stack in such a way we use the `calll` instruction, however it would also be possible to to use plain `jmpl` but would require to adjust the stack manually, which cannot be easily done, because the stack pointer argument is already stored in memory. */ movl 4(%esp), %esp calll *%edx ud2 .rust_psm_replace_stack_end: SIZE(rust_psm_replace_stack,.rust_psm_replace_stack_end) .cfi_endproc GLOBL(rust_psm_on_stack) .p2align 4 TYPE(rust_psm_on_stack) FUNCTION(rust_psm_on_stack): /* extern "fastcall" fn(%ecx: usize, %edx: usize, 4(%esp): extern "fastcall" fn(usize, usize), 8(%esp): *mut u8) */ .cfi_startproc pushl %ebp .cfi_def_cfa %esp, 8 .cfi_offset %ebp, -8 movl %esp, %ebp .cfi_def_cfa_register %ebp movl 12(%ebp), %esp calll *8(%ebp) movl %ebp, %esp popl %ebp .cfi_def_cfa %esp, 4 retl $8 .rust_psm_on_stack_end: SIZE(rust_psm_on_stack,.rust_psm_on_stack_end) .cfi_endproc psm-0.1.21/src/arch/x86_64.s000064400000000000000000000042731046102023000133400ustar 00000000000000#include "psm.h" /* NOTE: sysv64 calling convention is used on all x86_64 targets, including Windows! */ .text #if CFG_TARGET_OS_darwin || CFG_TARGET_OS_macos || CFG_TARGET_OS_ios #define GLOBL(fnname) .globl _##fnname #define TYPE(fnname) #define FUNCTION(fnname) _##fnname #define SIZE(fnname,endlabel) #else #define GLOBL(fnname) .globl fnname #define TYPE(fnname) .type fnname,@function #define FUNCTION(fnname) fnname #define SIZE(fnname,endlabel) .size fnname,endlabel-fnname #endif GLOBL(rust_psm_stack_direction) .p2align 4 TYPE(rust_psm_stack_direction) FUNCTION(rust_psm_stack_direction): /* extern "sysv64" fn() -> u8 (%al) */ .cfi_startproc movb $STACK_DIRECTION_DESCENDING, %al # always descending on x86_64 retq .rust_psm_stack_direction_end: SIZE(rust_psm_stack_direction,.rust_psm_stack_direction_end) .cfi_endproc GLOBL(rust_psm_stack_pointer) .p2align 4 TYPE(rust_psm_stack_pointer) FUNCTION(rust_psm_stack_pointer): /* extern "sysv64" fn() -> *mut u8 (%rax) */ .cfi_startproc leaq 8(%rsp), %rax retq .rust_psm_stack_pointer_end: SIZE(rust_psm_stack_pointer,.rust_psm_stack_pointer_end) .cfi_endproc GLOBL(rust_psm_replace_stack) .p2align 4 TYPE(rust_psm_replace_stack) FUNCTION(rust_psm_replace_stack): /* extern "sysv64" fn(%rdi: usize, %rsi: extern "sysv64" fn(usize), %rdx: *mut u8) */ .cfi_startproc /* All we gotta do is set the stack pointer to %rdx & tail-call the callback in %rsi. 8-byte offset necessary to account for the "return" pointer that would otherwise be placed onto stack with a regular call */ leaq -8(%rdx), %rsp jmpq *%rsi .rust_psm_replace_stack_end: SIZE(rust_psm_replace_stack,.rust_psm_replace_stack_end) .cfi_endproc GLOBL(rust_psm_on_stack) .p2align 4 TYPE(rust_psm_on_stack) FUNCTION(rust_psm_on_stack): /* extern "sysv64" fn(%rdi: usize, %rsi: usize, %rdx: extern "sysv64" fn(usize, usize), %rcx: *mut u8) */ .cfi_startproc pushq %rbp .cfi_def_cfa %rsp, 16 .cfi_offset %rbp, -16 movq %rsp, %rbp .cfi_def_cfa_register %rbp movq %rcx, %rsp callq *%rdx movq %rbp, %rsp popq %rbp .cfi_def_cfa %rsp, 8 retq .rust_psm_on_stack_end: SIZE(rust_psm_on_stack,.rust_psm_on_stack_end) .cfi_endproc psm-0.1.21/src/arch/x86_64_msvc.asm000064400000000000000000000030411046102023000146760ustar 00000000000000PUBLIC rust_psm_stack_direction PUBLIC rust_psm_stack_pointer PUBLIC rust_psm_replace_stack PUBLIC rust_psm_on_stack _TEXT SEGMENT ; extern "sysv64" fn() -> u8 (%al) rust_psm_stack_direction PROC mov al, 2 ret rust_psm_stack_direction ENDP ; extern "sysv64" fn() -> *mut u8 (%rax) rust_psm_stack_pointer PROC lea rax, [rsp + 8] ret rust_psm_stack_pointer ENDP ; extern "sysv64" fn(%rdi: usize, %rsi: extern "sysv64" fn(usize), %rdx: *mut u8, %rcx: *mut u8) rust_psm_replace_stack PROC mov gs:[08h], rdx mov gs:[10h], rcx lea rsp, [rdx - 8] jmp rsi rust_psm_replace_stack ENDP ; extern "sysv64" fn(%rdi: usize, %rsi: usize, ; %rdx: extern "sysv64" fn(usize, usize), %rcx: *mut u8, %r8: *mut u8) ; ; NB: on Windows for SEH to work at all, the pointers in TIB, thread information block, need to be ; fixed up. Otherwise, it seems that exception mechanism on Windows will not bother looking for ; exception handlers at *all* if they happen to fall outside the are specified in TIB. ; ; This necessitates an API difference from the usual 4-argument signature used elsewhere. ; ; FIXME: this needs a catch-all exception handler that aborts in case somebody unwinds into here. rust_psm_on_stack PROC FRAME push rbp .pushreg rbp mov rbp, rsp .setframe rbp, 0 .endprolog push gs:[08h] mov gs:[08h], rcx push gs:[10h] mov gs:[10h], r8 mov rsp, rcx call rdx lea rsp, [rbp - 010h] pop gs:[10h] pop gs:[08h] pop rbp ret rust_psm_on_stack ENDP _TEXT ENDS END psm-0.1.21/src/arch/x86_64_windows_gnu.s000064400000000000000000000042521046102023000157600ustar 00000000000000.text .def rust_psm_stack_direction .scl 2 .type 32 .endef .globl rust_psm_stack_direction .p2align 4 rust_psm_stack_direction: /* extern "sysv64" fn() -> u8 (%al) */ .cfi_startproc movb $2, %al # always descending on x86_64 retq .cfi_endproc .def rust_psm_stack_pointer .scl 2 .type 32 .endef .globl rust_psm_stack_pointer .p2align 4 rust_psm_stack_pointer: /* extern "sysv64" fn() -> *mut u8 (%rax) */ .cfi_startproc leaq 8(%rsp), %rax retq .cfi_endproc .def rust_psm_replace_stack .scl 2 .type 32 .endef .globl rust_psm_replace_stack .p2align 4 rust_psm_replace_stack: /* extern "sysv64" fn(%rdi: usize, %rsi: extern "sysv64" fn(usize), %rdx: *mut u8, %rcx: *mut u8) */ .cfi_startproc /* All we gotta do is set the stack pointer to %rdx & tail-call the callback in %rsi. 8-byte offset necessary to account for the "return" pointer that would otherwise be placed onto stack with a regular call */ movq %gs:0x08, %rdx movq %gs:0x10, %rcx leaq -8(%rdx), %rsp jmpq *%rsi .cfi_endproc .def rust_psm_on_stack .scl 2 .type 32 .endef .globl rust_psm_on_stack .p2align 4 rust_psm_on_stack: /* extern "sysv64" fn(%rdi: usize, %rsi: usize, %rdx: extern "sysv64" fn(usize, usize), %rcx: *mut u8, %r8: *mut u8) NB: on Windows for SEH to work at all, the pointers in TIB, thread information block, need to be fixed up. Otherwise, it seems that exception mechanism on Windows will not bother looking for exception handlers at *all* if they happen to fall outside the are specified in TIB. This necessitates an API difference from the usual 4-argument signature used elsewhere. FIXME: this needs a catch-all exception handler that aborts in case somebody unwinds into here. */ .cfi_startproc pushq %rbp .cfi_def_cfa %rsp, 16 .cfi_offset %rbp, -16 pushq %gs:0x08 .cfi_def_cfa %rsp, 24 pushq %gs:0x10 .cfi_def_cfa %rsp, 32 movq %rsp, %rbp .cfi_def_cfa_register %rbp movq %rcx, %gs:0x08 movq %r8, %gs:0x10 movq %rcx, %rsp callq *%rdx movq %rbp, %rsp popq %gs:0x10 .cfi_def_cfa %rsp, 24 popq %gs:0x08 .cfi_def_cfa %rsp, 16 popq %rbp .cfi_def_cfa %rsp, 8 retq .cfi_endproc psm-0.1.21/src/arch/x86_msvc.asm000064400000000000000000000036651046102023000144010ustar 00000000000000; FIXME: this is weird, this works locally but not on appveyor?!??! .386 .model flat ASSUME FS:NOTHING ; WTF: PUBLIC conflicts with "SYSCALL" but "SYSCALL" is the only way to stop MASM from manging the ; symbol names? ; ; PUBLIC @rust_psm_stack_direction@0 ; PUBLIC @rust_psm_stack_pointer@0 ; PUBLIC @rust_psm_replace_stack@12 ; PUBLIC @rust_psm_on_stack@16 _TEXT SEGMENT ; extern "fastcall" fn() -> u8 (%al) @rust_psm_stack_direction@0 PROC SYSCALL mov al, 2 ret @rust_psm_stack_direction@0 ENDP ; extern "fastcall" fn() -> *mut u8 (%rax) @rust_psm_stack_pointer@0 PROC SYSCALL lea eax, [esp + 4] ret @rust_psm_stack_pointer@0 ENDP ; extern "fastcall" fn(%ecx: usize, %edx: extern "fastcall" fn(usize), ; 4(%esp): *mut u8, 8(%esp): *mut u8) @rust_psm_replace_stack@16 PROC SYSCALL mov eax, dword ptr [esp + 8] mov fs:[08h], eax mov esp, dword ptr [esp + 4] mov fs:[04h], esp jmp edx @rust_psm_replace_stack@16 ENDP ; extern "fastcall" fn(%ecx: usize, %edx: usize, 4(%esp): extern "fastcall" fn(usize, usize), ; 8(%esp): *mut u8, 12(%esp): *mut u8) ; ; NB: on Windows for SEH to work at all, the pointers in TIB, thread information block, need to be ; fixed up. Otherwise, it seems that exception mechanism on Windows will not bother looking for ; exception handlers at *all* if they happen to fall outside the are specified in TIB. ; ; This necessitates an API difference from the usual 4-argument signature used elsewhere. @rust_psm_on_stack@20 PROC SYSCALL push ebp mov ebp, esp push fs:[0E0Ch] push fs:[08h] mov eax, dword ptr [ebp + 4 + 12] mov dword ptr fs:[08h], eax mov dword ptr fs:[0E0Ch], eax push fs:[04h] mov esp, dword ptr [ebp + 4 + 8] mov dword ptr fs:[04h], esp call dword ptr [ebp + 4 + 4] lea esp, [ebp - 12] pop fs:[04h] pop fs:[08h] pop fs:[0E0Ch] pop ebp ret 12 @rust_psm_on_stack@20 ENDP END psm-0.1.21/src/arch/x86_windows_gnu.s000064400000000000000000000043301046102023000154440ustar 00000000000000/* FIXME: this works locally but not on appveyor??!? */ /* NOTE: fastcall calling convention used on all x86 targets */ .text .def @rust_psm_stack_direction@0 .scl 2 .type 32 .endef .globl @rust_psm_stack_direction@0 .p2align 4 @rust_psm_stack_direction@0: /* extern "fastcall" fn() -> u8 (%al) */ .cfi_startproc movb $2, %al # always descending on x86_64 retl .cfi_endproc .def @rust_psm_stack_pointer@0 .scl 2 .type 32 .endef .globl @rust_psm_stack_pointer@0 .p2align 4 @rust_psm_stack_pointer@0: /* extern "fastcall" fn() -> *mut u8 (%rax) */ .cfi_startproc leal 4(%esp), %eax retl .cfi_endproc .def @rust_psm_replace_stack@16 .scl 2 .type 32 .endef .globl @rust_psm_replace_stack@16 .p2align 4 @rust_psm_replace_stack@16: /* extern "fastcall" fn(%ecx: usize, %edx: extern "fastcall" fn(usize), 4(%esp): *mut u8) */ .cfi_startproc /* All we gotta do is set the stack pointer to 4(%esp) & tail-call the callback in %edx Note, that the callee expects the stack to be offset by 4 bytes (normally, a return address would be store there) off the required stack alignment on entry. To offset the stack in such a way we use the `calll` instruction, however it would also be possible to to use plain `jmpl` but would require to adjust the stack manually, which cannot be easily done, because the stack pointer argument is already stored in memory. */ movl 8(%esp), %eax mov %eax, %fs:0x08 movl 4(%esp), %esp mov %esp, %fs:0x04 calll *%edx ud2 .cfi_endproc .def @rust_psm_on_stack@16 .scl 2 .type 32 .endef .globl @rust_psm_on_stack@16 .p2align 4 @rust_psm_on_stack@16: /* extern "fastcall" fn(%ecx: usize, %edx: usize, 4(%esp): extern "fastcall" fn(usize, usize), 8(%esp): *mut u8) */ .cfi_startproc pushl %ebp .cfi_def_cfa %esp, 8 .cfi_offset %ebp, -8 pushl %fs:0x04 .cfi_def_cfa %esp, 12 pushl %fs:0x08 .cfi_def_cfa %esp, 16 movl %esp, %ebp .cfi_def_cfa_register %ebp movl 24(%ebp), %eax movl %eax, %fs:0x08 movl 20(%ebp), %esp movl %esp, %fs:0x04 calll *16(%ebp) movl %ebp, %esp popl %fs:0x08 .cfi_def_cfa %esp, 12 popl %fs:0x04 .cfi_def_cfa %esp, 8 popl %ebp .cfi_def_cfa %esp, 4 retl $12 .cfi_endproc psm-0.1.21/src/arch/zseries_linux.s000064400000000000000000000040401046102023000152750ustar 00000000000000/* Implementation of stack swtiching routines for zSeries LINUX ABI. This ABI is used by the s390x-unknown-linux-gnu target. Documents used: * LINUX for zSeries: ELF Application Binary Interface Supplement (1st ed., 2001) (LNUX-1107-01) * z/Architecture: Principles of Operation (4th ed., 2004) (SA22-7832-03) */ #include "psm.h" .text .globl rust_psm_stack_direction .p2align 4 .type rust_psm_stack_direction,@function rust_psm_stack_direction: /* extern "C" fn() -> u8 */ .cfi_startproc lghi %r2, STACK_DIRECTION_DESCENDING br %r14 .rust_psm_stack_direction_end: .size rust_psm_stack_direction,.rust_psm_stack_direction_end-rust_psm_stack_direction .cfi_endproc .globl rust_psm_stack_pointer .p2align 4 .type rust_psm_stack_pointer,@function rust_psm_stack_pointer: /* extern "C" fn() -> *mut u8 */ .cfi_startproc la %r2, 0(%r15) br %r14 .rust_psm_stack_pointer_end: .size rust_psm_stack_pointer,.rust_psm_stack_pointer_end-rust_psm_stack_pointer .cfi_endproc .globl rust_psm_replace_stack .p2align 4 .type rust_psm_replace_stack,@function rust_psm_replace_stack: /* extern "C" fn(r2: usize, r3: extern "C" fn(usize), r4: *mut u8) */ .cfi_startproc /* FIXME: backtrace does not terminate cleanly for some reason */ lay %r15, -160(%r4) /* FIXME: this is `basr` instead of `br` purely to remove the backtrace link to the caller */ basr %r14, %r3 .rust_psm_replace_stack_end: .size rust_psm_replace_stack,.rust_psm_replace_stack_end-rust_psm_replace_stack .cfi_endproc .globl rust_psm_on_stack .p2align 4 .type rust_psm_on_stack,@function rust_psm_on_stack: /* extern "C" fn(r2: usize, r3: usize, r4: extern "C" fn(usize, usize), r5: *mut u8) */ .cfi_startproc stmg %r14, %r15, -16(%r5) lay %r15, -176(%r5) .cfi_def_cfa %r15, 176 .cfi_offset %r14, -16 .cfi_offset %r15, -8 basr %r14, %r4 lmg %r14, %r15, 160(%r15) .cfi_restore %r14 .cfi_restore %r15 br %r14 .rust_psm_on_stack_end: .size rust_psm_on_stack,.rust_psm_on_stack_end-rust_psm_on_stack .cfi_endproc psm-0.1.21/src/lib.rs000064400000000000000000000351151046102023000124140ustar 00000000000000//! # **P**ortable **S**tack **M**anipulation //! This crate provides portable functions to control the stack pointer and inspect the properties //! of the stack. This crate does not attempt to provide safe abstractions to any operations, the //! only goals are correctness, portability and efficiency (in that exact order). As a consequence //! most functions you will find in this crate are unsafe. //! //! Note, that the stack allocation is left up to the user. Unless you’re writing a safe //! abstraction over stack manipulation, this is unlikely to be the crate you want. Instead //! consider one of the safe abstractions over this crate such as `stacker`. Another good place to //! look at is the crates.io’s reverse dependency list. #![allow(unused_macros)] #![no_std] macro_rules! extern_item { (unsafe $($toks: tt)+) => { unsafe extern "C" $($toks)+ }; ($($toks: tt)+) => { extern "C" $($toks)+ }; } // Surprising: turns out subsequent macro_rules! override previous definitions, instead of // erroring? Convenient for us in this case, though. #[cfg(target_arch = "x86_64")] macro_rules! extern_item { (unsafe $($toks: tt)+) => { unsafe extern "sysv64" $($toks)+ }; ($($toks: tt)+) => { extern "sysv64" $($toks)+ }; } #[cfg(target_arch = "x86")] macro_rules! extern_item { (unsafe $($toks: tt)+) => { unsafe extern "fastcall" $($toks)+ }; ($($toks: tt)+) => { extern "fastcall" $($toks)+ }; } #[cfg(target_arch = "arm")] macro_rules! extern_item { (unsafe $($toks: tt)+) => { unsafe extern "aapcs" $($toks)+ }; ($($toks: tt)+) => { extern "aapcs" $($toks)+ }; } // NB: this could be nicer across multiple blocks but we cannot do it because of // https://github.com/rust-lang/rust/issues/65847 extern_item! { { #![cfg_attr(asm, link(name="psm_s"))] #[cfg(asm)] fn rust_psm_stack_direction() -> u8; #[cfg(asm)] fn rust_psm_stack_pointer() -> *mut u8; #[cfg(all(switchable_stack, not(target_os = "windows")))] #[link_name="rust_psm_replace_stack"] fn _rust_psm_replace_stack( data: usize, callback: extern_item!(unsafe fn(usize) -> !), sp: *mut u8 ) -> !; #[cfg(all(switchable_stack, not(target_os = "windows")))] #[link_name="rust_psm_on_stack"] fn _rust_psm_on_stack( data: usize, return_ptr: usize, callback: extern_item!(unsafe fn(usize, usize)), sp: *mut u8, ); #[cfg(all(switchable_stack, target_os = "windows"))] fn rust_psm_replace_stack( data: usize, callback: extern_item!(unsafe fn(usize) -> !), sp: *mut u8, stack_base: *mut u8 ) -> !; #[cfg(all(switchable_stack, target_os = "windows"))] fn rust_psm_on_stack( data: usize, return_ptr: usize, callback: extern_item!(unsafe fn(usize, usize)), sp: *mut u8, stack_base: *mut u8 ); } } #[cfg(all(switchable_stack, not(target_os = "windows")))] #[inline(always)] unsafe fn rust_psm_replace_stack( data: usize, callback: extern_item!(unsafe fn(usize) -> !), sp: *mut u8, _: *mut u8, ) -> ! { _rust_psm_replace_stack(data, callback, sp) } #[cfg(all(switchable_stack, not(target_os = "windows")))] #[inline(always)] unsafe fn rust_psm_on_stack( data: usize, return_ptr: usize, callback: extern_item!(unsafe fn(usize, usize)), sp: *mut u8, _: *mut u8, ) { _rust_psm_on_stack(data, return_ptr, callback, sp) } /// Run the closure on the provided stack. /// /// Once the closure completes its execution, the original stack pointer is restored and execution /// returns to the caller. /// /// `base` address must be the low address of the stack memory region, regardless of the stack /// growth direction. It is not necessary for the whole region `[base; base + size]` to be usable /// at the time this function called, however it is required that at least the following hold: /// /// * Both `base` and `base + size` are aligned up to the target-specific requirements; /// * Depending on `StackDirection` value for the platform, the end of the stack memory region, /// which would end up containing the first frame(s), must have sufficient number of pages /// allocated to execute code until more pages are commited. The other end should contain a guard /// page (not writable, readable or executable) to ensure Rust’s soundness guarantees. /// /// Note, that some or all of these considerations are irrelevant to some applications. For /// example, Rust’s soundness story relies on all stacks having a guard-page, however if the user /// is able to guarantee that the memory region used for stack cannot be exceeded, a guard page may /// end up being an expensive unnecessity. /// /// The previous stack may not be deallocated. If an ability to deallocate the old stack is desired /// consider `replace_stack` instead. /// /// # Guidelines /// /// Memory regions that are aligned to a single page (usually 4kB) are an extremely portable choice /// for stacks. /// /// Allocate at least 4kB of stack. Some architectures (such as SPARC) consume stack memory /// significantly faster compared to the more usual architectures such as x86 or ARM. Allocating /// less than 4kB of memory may make it impossible to commit more pages without overflowing the /// stack later on. /// /// # Unsafety /// /// The stack `base` address must be aligned as appropriate for the target. /// /// The stack `size` must be a multiple of stack alignment required by target. /// /// The `size` must not overflow `isize`. /// /// `callback` must not unwind or return control flow by any other means than directly returning. /// /// # Examples /// /// ``` /// use std::alloc; /// const STACK_ALIGN: usize = 4096; /// const STACK_SIZE: usize = 4096; /// unsafe { /// let layout = alloc::Layout::from_size_align(STACK_SIZE, STACK_ALIGN).unwrap(); /// let new_stack = alloc::alloc(layout); /// assert!(!new_stack.is_null(), "allocations must succeed!"); /// let (stack, result) = psm::on_stack(new_stack, STACK_SIZE, || { /// (psm::stack_pointer(), 4 + 4) /// }); /// println!("4 + 4 = {} has been calculated on stack {:p}", result, stack); /// } /// ``` #[cfg(switchable_stack)] pub unsafe fn on_stack R>(base: *mut u8, size: usize, callback: F) -> R { use core::mem::MaybeUninit; extern_item! { unsafe fn with_on_stack R>(callback_ptr: usize, return_ptr: usize) { let return_ptr = (*(return_ptr as *mut MaybeUninit)).as_mut_ptr(); let callback = (*(callback_ptr as *mut MaybeUninit)).as_ptr(); // Safe to move out from `F`, because closure in is forgotten in `on_stack` and dropping // only occurs in this callback. return_ptr.write((callback.read())()); } } let sp = match StackDirection::new() { StackDirection::Ascending => base, StackDirection::Descending => base.offset(size as isize), }; let mut callback: MaybeUninit = MaybeUninit::new(callback); let mut return_value: MaybeUninit = MaybeUninit::uninit(); rust_psm_on_stack( &mut callback as *mut MaybeUninit as usize, &mut return_value as *mut MaybeUninit as usize, with_on_stack::, sp, base, ); return return_value.assume_init(); } /// Run the provided non-terminating computation on an entirely new stack. /// /// `base` address must be the low address of the stack memory region, regardless of the stack /// growth direction. It is not necessary for the whole region `[base; base + size]` to be usable /// at the time this function called, however it is required that at least the following hold: /// /// * Both `base` and `base + size` are aligned up to the target-specific requirements; /// * Depending on `StackDirection` value for the platform, the end of the stack memory region, /// which would end up containing the first frame(s), must have sufficient number of pages /// allocated to execute code until more pages are commited. The other end should contain a guard /// page (not writable, readable or executable) to ensure Rust’s soundness guarantees. /// /// Note, that some or all of these considerations are irrelevant to some applications. For /// example, Rust’s soundness story relies on all stacks having a guard-page, however if the user /// is able to guarantee that the memory region used for stack cannot be exceeded, a guard page may /// end up being an expensive unnecessity. /// /// The previous stack is not deallocated and may not be deallocated unless the data on the old /// stack is not referenced in any way (by e.g. the `callback` closure). /// /// On platforms where multiple stack pointers are available, the “current” stack pointer is /// replaced. /// /// # Guidelines /// /// Memory regions that are aligned to a single page (usually 4kB) are an extremely portable choice /// for stacks. /// /// Allocate at least 4kB of stack. Some architectures (such as SPARC) consume stack memory /// significantly faster compared to the more usual architectures such as x86 or ARM. Allocating /// less than 4kB of memory may make it impossible to commit more pages without overflowing the /// stack later on. /// /// # Unsafety /// /// The stack `base` address must be aligned as appropriate for the target. /// /// The stack `size` must be a multiple of stack alignment required by target. /// /// The `size` must not overflow `isize`. /// /// `callback` must not return (not enforced by typesystem currently because `!` is unstable), /// unwind or otherwise return control flow to any of the previous frames. #[cfg(switchable_stack)] pub unsafe fn replace_stack(base: *mut u8, size: usize, callback: F) -> ! { extern_item! { unsafe fn with_replaced_stack(d: usize) -> ! { // Safe to move out, because the closure is essentially forgotten by // this being required to never return... ::core::ptr::read(d as *const F)(); ::core::hint::unreachable_unchecked(); } } let sp = match StackDirection::new() { StackDirection::Ascending => base, StackDirection::Descending => base.offset(size as isize), }; rust_psm_replace_stack( &callback as *const F as usize, with_replaced_stack::, sp, base, ); } /// The direction into which stack grows as stack frames are made. /// /// This is a target-specific property that can be obtained at runtime by calling /// `StackDirection::new()`. #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum StackDirection { Ascending = 1, Descending = 2, } impl StackDirection { /// Obtain the stack growth direction. #[cfg(asm)] pub fn new() -> StackDirection { const ASC: u8 = StackDirection::Ascending as u8; const DSC: u8 = StackDirection::Descending as u8; unsafe { match rust_psm_stack_direction() { ASC => StackDirection::Ascending, DSC => StackDirection::Descending, _ => ::core::hint::unreachable_unchecked(), } } } } /// Returns current stack pointer. /// /// Note, that the stack pointer returned is from the perspective of the caller. From the /// perspective of `stack_pointer` function the pointer returned is the frame pointer. /// /// While it is a goal to minimize the amount of stack used by this function, implementations for /// some targets may be unable to avoid allocating a stack frame. This makes this function /// suitable for stack exhaustion detection only in conjunction with sufficient padding. /// /// Using `stack_pointer` to check for stack exhaustion is tricky to get right. It is impossible to /// know the callee’s frame size, therefore such value must be derived some other way. A common /// approach is to use stack padding (reserve enough stack space for any function to be called) and /// check against the padded threshold. If padding is chosen incorrectly, a situation similar to /// one described below may occur: /// /// 1. For stack exhaustion check, remaining stack is checked against `stack_pointer` with the /// padding applied; /// 2. Callee allocates more stack than was accounted for with padding, and accesses pages outside /// the stack, invalidating the execution (by e.g. crashing). #[cfg(asm)] pub fn stack_pointer() -> *mut u8 { unsafe { rust_psm_stack_pointer() } } /// Macro that outputs its tokens only if `psm::on_stack` and `psm::replace_stack` are available. /// /// # Examples /// /// ``` /// # use psm::psm_stack_manipulation; /// psm_stack_manipulation! { /// yes { /// /* Functions `on_stack` and `replace_stack` are available here */ /// } /// no { /// /* Functions `on_stack` and `replace_stack` are not available here */ /// } /// } /// ``` #[cfg(switchable_stack)] #[macro_export] macro_rules! psm_stack_manipulation { (yes { $($yes: tt)* } no { $($no: tt)* }) => { $($yes)* }; } /// Macro that outputs its tokens only if `psm::on_stack` and `psm::replace_stack` are available. /// /// # Examples /// /// ``` /// # use psm::psm_stack_manipulation; /// psm_stack_manipulation! { /// yes { /// /* Functions `on_stack` and `replace_stack` are available here */ /// } /// no { /// /* Functions `on_stack` and `replace_stack` are not available here */ /// } /// } /// ``` #[cfg(not(switchable_stack))] #[macro_export] macro_rules! psm_stack_manipulation { (yes { $($yes: tt)* } no { $($no: tt)* }) => { $($no)* }; } /// Macro that outputs its tokens only if `psm::stack_pointer` and `psm::StackDirection::new` are /// available. /// /// # Examples /// /// ``` /// # use psm::psm_stack_information; /// psm_stack_information! { /// yes { /// /* `psm::stack_pointer` and `psm::StackDirection::new` are available here */ /// } /// no { /// /* `psm::stack_pointer` and `psm::StackDirection::new` are not available here */ /// } /// } /// ``` #[cfg(asm)] #[macro_export] macro_rules! psm_stack_information { (yes { $($yes: tt)* } no { $($no: tt)* }) => { $($yes)* }; } /// Macro that outputs its tokens only if `psm::stack_pointer` and `psm::StackDirection::new` are /// available. /// /// # Examples /// /// ``` /// # use psm::psm_stack_information; /// psm_stack_information! { /// yes { /// /* `psm::stack_pointer` and `psm::StackDirection::new` are available here */ /// } /// no { /// /* `psm::stack_pointer` and `psm::StackDirection::new` are not available here */ /// } /// } /// ``` #[cfg(not(asm))] #[macro_export] macro_rules! psm_stack_information { (yes { $($yes: tt)* } no { $($no: tt)* }) => { $($no)* }; } psm-0.1.21/tests/stack_direction.rs000064400000000000000000000001711046102023000153600ustar 00000000000000extern crate psm; #[test] fn always_equal() { assert_eq!(psm::StackDirection::new(), psm::StackDirection::new()); } psm-0.1.21/tests/stack_direction_2.rs000064400000000000000000000014341046102023000156040ustar 00000000000000extern crate psm; #[inline(never)] fn test_direction(previous_sp: *mut u8) { let current_sp = psm::stack_pointer(); match psm::StackDirection::new() { psm::StackDirection::Ascending => { assert!( current_sp > previous_sp, "the stack pointer is not ascending! current = {:p}, previous = {:p}", current_sp, previous_sp ); } psm::StackDirection::Descending => { assert!( current_sp < previous_sp, "the stack pointer is not descending! current = {:p}, previous = {:p}", current_sp, previous_sp ); } } } #[test] fn direction_right() { test_direction(psm::stack_pointer()); }