From 9bf3f60a7f1d27c54d8b8d33a7ae4f5658ace953 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sun, 30 Oct 2022 18:59:11 +0200 Subject: [PATCH 1/4] Setup a workspace, adjust READMEs --- Cargo.toml | 38 +--------------- README.md | 44 ------------------ README.mkd | 20 ++++++++ psm/README.mkd | 58 +++++++++++++----------- psm/src/lib.rs | 12 +---- stacker/Cargo.toml | 36 +++++++++++++++ LICENSE-APACHE => stacker/LICENSE-APACHE | 0 LICENSE-MIT => stacker/LICENSE-MIT | 0 stacker/README.mkd | 49 ++++++++++++++++++++ build.rs => stacker/build.rs | 0 {src => stacker/src}/arch/asm.h | 0 {src => stacker/src}/arch/windows.c | 0 {src => stacker/src}/lib.rs | 25 +--------- {tests => stacker/tests}/simple.rs | 0 {tests => stacker/tests}/smoke.rs | 0 15 files changed, 141 insertions(+), 141 deletions(-) delete mode 100644 README.md create mode 100644 README.mkd create mode 100644 stacker/Cargo.toml rename LICENSE-APACHE => stacker/LICENSE-APACHE (100%) rename LICENSE-MIT => stacker/LICENSE-MIT (100%) create mode 100644 stacker/README.mkd rename build.rs => stacker/build.rs (100%) rename {src => stacker/src}/arch/asm.h (100%) rename {src => stacker/src}/arch/windows.c (100%) rename {src => stacker/src}/lib.rs (94%) rename {tests => stacker/tests}/simple.rs (100%) rename {tests => stacker/tests}/smoke.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 306d990..e065ea0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,36 +1,2 @@ -[package] -name = "stacker" -version = "0.1.15" -authors = ["Alex Crichton ", "Simonas Kazlauskas "] -build = "build.rs" -license = "MIT OR Apache-2.0" -readme = "README.md" -repository = "https://github.com/rust-lang/stacker" -homepage = "https://github.com/rust-lang/stacker" -documentation = "https://docs.rs/stacker/0.1.15" -description = """ -A stack growth library useful when implementing deeply recursive algorithms that -may accidentally blow the stack. -""" - -[lib] -name = "stacker" -doctest = false -test = false - -[dependencies] -cfg-if = "1.0.0" -libc = "0.2.45" -psm = { path = "psm", version = "0.1.7" } - -[target.'cfg(windows)'.dependencies.windows-sys] -version = ">=0.34.0, <0.42.0" -features = [ - "Win32_System_Memory", - "Win32_System_Threading", - "Win32_Foundation", -] - - -[build-dependencies] -cc = "1.0.2" +[workspace] +members = ["stacker", "psm"] diff --git a/README.md b/README.md deleted file mode 100644 index 94858e4..0000000 --- a/README.md +++ /dev/null @@ -1,44 +0,0 @@ -# stacker - -[![Build Status](https://github.com/rust-lang/stacker/workflows/Test%20stacker/badge.svg)](https://github.com/rust-lang/stacker/actions) - -[Documentation](https://docs.rs/stacker) - -A stack-growth library for Rust. Enables annotating fixed points in programs -where the stack may want to grow larger. Spills over to the heap if the stack -has hit its limit. - -This library is intended on helping implement recursive algorithms. - -```toml -# Cargo.toml -[dependencies] -stacker = "0.1" -``` - -## Platform Support - -This library currently uses psm for its cross platform capabilities, with a notable exception of -Windows, which uses an implementation based on Fibers. See the README for psm for the support -table. - -On all unsupported platforms this library is a noop. It should compile and run, but it -won't actually grow the stack and code will continue to hit the guard pages -typically in place. - -# License - -This project 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. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in this project by you, as defined in the Apache-2.0 license, -shall be dual licensed as above, without any additional terms or conditions. diff --git a/README.mkd b/README.mkd new file mode 100644 index 0000000..db60909 --- /dev/null +++ b/README.mkd @@ -0,0 +1,20 @@ +
+

stacker & psm

+
+ +Check out the README for corresponding crates for further information on each crate. + +# License + +This project is licensed under either of + + * [Apache License, Version 2.0]() + * [MIT license]() + +at your option. + +## Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this project by you, as defined in the Apache-2.0 license, +shall be dual licensed as above, without any additional terms or conditions. diff --git a/psm/README.mkd b/psm/README.mkd index 5bc8957..f797aba 100644 --- a/psm/README.mkd +++ b/psm/README.mkd @@ -1,31 +1,39 @@ -# 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. +
+

psm

+

+ A portable library of stack introspection and manipulation operations +

+
+ +This crate provides functions to control the stack pointer and inspect the properties of the stack. +This crate does not attempt to provide safe or Rust-idiomatic abstractions to any of the +operations. The only goals are correctness, portability and efficiency (in this exact order). As a +consequence most functions you’ll see in this crate are unsafe and require the caller to maintain a +variety of invariants. + +Unless you are writing a safe abstraction over stack manipulation or need extremely precse control +over your stack, this is probably not the crate you want. Instead consider one of the safe +abstractions based on 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. +The following table lists targets and architectures supported by this crate, alongside the notes on +the current level of support and behaviour. The three columns “Available”, “Tested” and “Callstack” +indicate different degrees 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). +* On platforms marked “available” this library builds and contains necessary functionality to + introspect or manipulate the stack (see the `psm_stack_information` and `psm_stack_manipulation` + macros); +* “Tested” platforms have had their implementations tested or otherwise verified to be correct. For + many such targets it also means that we have a continuous integration setup; +* “Callstack” indicates that, in addition to the library being tested, it has been verified + functions based on stack unwinding (e.g. `gdb backtrace`) continue to work correctly. + - - + @@ -510,11 +518,9 @@ The assembly code for loongarch64 has been tested locally with a C caller. # License -PSM is licensed under either of +`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) + * [Apache License, Version 2.0]() + * [MIT license]() at your option. diff --git a/psm/src/lib.rs b/psm/src/lib.rs index b9050c8..c83ca3a 100644 --- a/psm/src/lib.rs +++ b/psm/src/lib.rs @@ -1,14 +1,4 @@ -//! # **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. - +#![doc=include_str!("../README.mkd")] #![allow(unused_macros)] #![no_std] diff --git a/stacker/Cargo.toml b/stacker/Cargo.toml new file mode 100644 index 0000000..01acf63 --- /dev/null +++ b/stacker/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "stacker" +version = "0.1.15" +authors = ["Alex Crichton ", "Simonas Kazlauskas "] +build = "build.rs" +license = "MIT OR Apache-2.0" +readme = "README.md" +repository = "https://github.com/rust-lang/stacker" +homepage = "https://github.com/rust-lang/stacker" +documentation = "https://docs.rs/stacker/0.1.15" +description = """ +A stack growth library useful when implementing deeply recursive algorithms that +may accidentally blow the stack. +""" + +[lib] +name = "stacker" +doctest = false +test = false + +[dependencies] +cfg-if = "1.0.0" +libc = "0.2.45" +psm = { path = "../psm", version = "0.1.7" } + +[target.'cfg(windows)'.dependencies.windows-sys] +version = ">=0.34.0, <0.42.0" +features = [ + "Win32_System_Memory", + "Win32_System_Threading", + "Win32_Foundation", +] + + +[build-dependencies] +cc = "1.0.2" diff --git a/LICENSE-APACHE b/stacker/LICENSE-APACHE similarity index 100% rename from LICENSE-APACHE rename to stacker/LICENSE-APACHE diff --git a/LICENSE-MIT b/stacker/LICENSE-MIT similarity index 100% rename from LICENSE-MIT rename to stacker/LICENSE-MIT diff --git a/stacker/README.mkd b/stacker/README.mkd new file mode 100644 index 0000000..1e650ed --- /dev/null +++ b/stacker/README.mkd @@ -0,0 +1,49 @@ +
+

stacker

+

+ A stack-growth library for Rust +

+
+ + +`stacker` enables users to annotate fixed points in their programs where the stack may want to grow +larger to accomodate for recursion or other kinds of heavy stack space usage. At each such +annotation, the caller indicates how far away from the end of the stack it's allowed to be, plus +the amount of stack to allocate if the remaining capacity is insufficient. + +Once a program has reached the end of its stack, a temporary stack on the heap is allocated and +is switched to for the duration of a closure. + +A philosophy behind this crate is to expose a straightforward interface. In case the functionality +provided by this crate aren’t flexible enough for your use-case, check out the `psm` crate upon +which `stacker` is built. + +# Examples + +``` +// Grow the stack if we are within the "red zone" of 32K, and if we allocate +// a new stack allocate an additional 1MB of stack space. +// +// If we're already within bounds, the provided closure will run on the current stack. +stacker::maybe_grow(32 * 1024, 1024 * 1024, || { + // guaranteed to have at least 32K of stack available to use. +}); +``` + +## Platform Support + +This library currently uses `psm` for its cross platform capabilities, with a notable exception of +Windows, which uses an implementation based on Fibers. See the documentation of psm for the table +of supported platforms. + +On all unsupported platforms this library is a noop. It should compile and run, but it won't +actually grow the stack and code will continue to hit the guard pages typically in place. + +# License + +`stacker` is licensed under either of + + * [Apache License, Version 2.0]() + * [MIT license]() + +at your option. diff --git a/build.rs b/stacker/build.rs similarity index 100% rename from build.rs rename to stacker/build.rs diff --git a/src/arch/asm.h b/stacker/src/arch/asm.h similarity index 100% rename from src/arch/asm.h rename to stacker/src/arch/asm.h diff --git a/src/arch/windows.c b/stacker/src/arch/windows.c similarity index 100% rename from src/arch/windows.c rename to stacker/src/arch/windows.c diff --git a/src/lib.rs b/stacker/src/lib.rs similarity index 94% rename from src/lib.rs rename to stacker/src/lib.rs index eaef9f1..505293d 100644 --- a/src/lib.rs +++ b/stacker/src/lib.rs @@ -1,27 +1,4 @@ -//! A library to help grow the stack when it runs out of space. -//! -//! This is an implementation of manually instrumented segmented stacks where points in a program's -//! control flow are annotated with "maybe grow the stack here". Each point of annotation indicates -//! how far away from the end of the stack it's allowed to be, plus the amount of stack to allocate -//! if it does reach the end. -//! -//! Once a program has reached the end of its stack, a temporary stack on the heap is allocated and -//! is switched to for the duration of a closure. -//! -//! For a set of lower-level primitives, consider the `psm` crate. -//! -//! # Examples -//! -//! ``` -//! // Grow the stack if we are within the "red zone" of 32K, and if we allocate -//! // a new stack allocate 1MB of stack space. -//! // -//! // If we're already in bounds, just run the provided closure on current stack. -//! stacker::maybe_grow(32 * 1024, 1024 * 1024, || { -//! // guaranteed to have at least 32K of stack -//! }); -//! ``` - +#![doc=include_str!("../README.mkd")] #![allow(improper_ctypes)] #[macro_use] diff --git a/tests/simple.rs b/stacker/tests/simple.rs similarity index 100% rename from tests/simple.rs rename to stacker/tests/simple.rs diff --git a/tests/smoke.rs b/stacker/tests/smoke.rs similarity index 100% rename from tests/smoke.rs rename to stacker/tests/smoke.rs From 8a8c05dd5367a0a822befbc7eb2f5191e30bf672 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sun, 30 Oct 2022 19:49:12 +0200 Subject: [PATCH 2/4] Drop actions-rs actions Unmaintained, and manual `run` commands are more readable anyhow. --- .github/workflows/test.yml | 293 +++++++++++-------------------------- Cross.toml | 4 +- 2 files changed, 91 insertions(+), 206 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 25f9401..e1fa586 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,61 +1,49 @@ -name: Test +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true on: push: - branches: - - master - paths-ignore: - - '*.mkd' - - 'LICENSE' + branches: [master] + paths-ignore: ['*.mkd', 'LICENSE'] pull_request: types: [opened, reopened, synchronize] jobs: - native-test: - name: Test ${{ matrix.manifest }} on ${{ matrix.os }} with ${{ matrix.rust_toolchain }} and ${{ matrix.mode }} + test: + name: "Test: ${{ toJSON(matrix) }}" runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - rust_toolchain: [nightly, stable, 1.38.0] + rust_toolchain: [nightly, stable, 1.54.0] os: [ubuntu-latest, windows-latest, macOS-latest] mode: ['--release', '-Zminimal-versions', ''] - manifest: ['psm/Cargo.toml', 'Cargo.toml'] exclude: - rust_toolchain: stable mode: -Zminimal-versions - - rust_toolchain: 1.38.0 + - rust_toolchain: 1.54.0 mode: -Zminimal-versions timeout-minutes: 10 steps: - - uses: actions/checkout@v2 - - name: Install Rust ${{ matrix.rust_toolchain }} - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.rust_toolchain }} - profile: minimal - default: true - - name: Test ${{ matrix.manifest}} with ${{ matrix.mode }} - uses: actions-rs/cargo@v1 - with: - command: test - args: --manifest-path=${{ matrix.manifest }} ${{ matrix.mode }} -- --nocapture - - name: Test ${{ matrix.manifest}} examples with ${{ matrix.mode }} - uses: actions-rs/cargo@v1 - with: - command: test - args: --manifest-path=${{ matrix.manifest }} ${{ matrix.mode }} --examples -- --nocapture + - uses: actions/checkout@v3 + - run: rustup install ${{ matrix.rust_toolchain }} --profile=minimal + - run: rustup default ${{ matrix.rust_toolchain }} + - run: cargo test --all ${{ matrix.mode }} -- --nocapture + - run: cargo test --all ${{ matrix.mode }} --examples -- --nocapture - clang-cl-test: - name: Test ${{ matrix.manifest }} on ${{ matrix.rust_target }} with ${{ matrix.clang_cl }} + test-windows: + name: "Test: ${{ toJSON(matrix) }}" runs-on: windows-latest strategy: fail-fast: false matrix: - manifest: ['psm/Cargo.toml', 'Cargo.toml'] + rust_toolchain: [stable] rust_target: - x86_64-pc-windows-msvc - i686-pc-windows-msvc + - x86_64-pc-windows-gnu + - i686-pc-windows-gnu include: - rust_target: x86_64-pc-windows-msvc clang_cl: C:/msys64/mingw64/bin/clang-cl.exe @@ -63,37 +51,6 @@ jobs: - rust_target: i686-pc-windows-msvc clang_cl: C:/msys64/mingw32/bin/clang-cl.exe package: mingw-w64-i686-clang - steps: - - uses: actions/checkout@v2 - - uses: msys2/setup-msys2@v2 - with: - release: false - install: ${{ matrix.package }} - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - default: true - target: ${{ matrix.rust_target }} - - uses: actions-rs/cargo@v1 - with: - command: test - args: --target=${{ matrix.rust_target }} --manifest-path=${{ matrix.manifest }} -- --nocapture - env: - CC: ${{ matrix.clang_cl }} - - windows-gnu-test: - name: Test ${{ matrix.manifest }} on ${{ matrix.rust_target }} with ${{ matrix.rust_toolchain }} - runs-on: windows-latest - strategy: - fail-fast: false - matrix: - rust_toolchain: [nightly, stable] - rust_target: - - x86_64-pc-windows-gnu - - i686-pc-windows-gnu - manifest: ['psm/Cargo.toml', 'Cargo.toml'] - include: - rust_target: x86_64-pc-windows-gnu mingw_path: C:/msys64/mingw64/bin package: mingw-w64-x86_64-gcc @@ -101,137 +58,81 @@ jobs: mingw_path: C:/msys64/mingw32/bin package: mingw-w64-i686-gcc steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: msys2/setup-msys2@v2 with: release: false install: ${{ matrix.package }} - run: echo "c:/msys64/bin" | Out-File -FilePath $env:GITHUB_PATH -Append - run: echo "${{ matrix.mingw_path }}" | Out-File -FilePath $env:GITHUB_PATH -Append - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.rust_toolchain }} - profile: minimal - target: ${{ matrix.rust_target }} - default: true - - uses: actions-rs/cargo@v1 - with: - command: test - args: --target ${{ matrix.rust_target }} --manifest-path=${{ matrix.manifest }} + if: ${{ matrix.mingw_path }}" + - run: rustup install ${{ matrix.rust_toolchain }} --profile=minimal + - run: rustup default ${{ matrix.rust_toolchain }} + - run: rustup target add ${{ matrix.rust_target }} + - run: cargo test --target=${{ matrix.rust_target }} --all -- --nocapture + env: + CC: ${{ matrix.clang_cl }} - cross-linux-test: - name: Test ${{ matrix.manifest }} on ${{ matrix.rust_target }} with nightly ${{ matrix.mode }} + cross: + name: "Cross: ${{ toJSON(matrix) }}" runs-on: ubuntu-latest strategy: fail-fast: false matrix: + rust_toolchain: [stable, nightly] rust_target: - aarch64-linux-android - - arm-linux-androideabi - - armv7-linux-androideabi - - x86_64-linux-android - aarch64-unknown-linux-gnu + - arm-linux-androideabi - arm-unknown-linux-gnueabi + - armv7-linux-androideabi - armv7-unknown-linux-gnueabihf + - i686-unknown-freebsd - i686-unknown-linux-gnu - i686-unknown-linux-musl - - mips-unknown-linux-gnu - - mips64-unknown-linux-gnuabi64 - mips64el-unknown-linux-gnuabi64 + - mips64-unknown-linux-gnuabi64 - mipsel-unknown-linux-gnu + - mips-unknown-linux-gnu + - powerpc64le-unknown-linux-gnu + - powerpc64-unknown-linux-gnu - powerpc-unknown-linux-gnu - # https://github.com/rust-embedded/cross/pull/440 - # - powerpc64-unknown-linux-gnu + - s390x-unknown-linux-gnu + - sparc64-unknown-linux-gnu + - x86_64-linux-android + - x86_64-unknown-freebsd - x86_64-unknown-linux-musl - manifest: ['psm/Cargo.toml', 'Cargo.toml'] + - x86_64-unknown-netbsd mode: ['--release', '-Zminimal-versions', ''] - timeout-minutes: 10 - steps: - - uses: actions/checkout@v2 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - profile: minimal - target: ${{ matrix.rust_target }} - default: true - - name: Test - uses: actions-rs/cargo@v1 - with: - use-cross: true - command: test - args: --target ${{ matrix.rust_target }} --manifest-path=${{ matrix.manifest }} ${{ matrix.mode }} -- --test-threads=1 --nocapture - native-build: - name: Build ${{ matrix.manifest }} to ${{ matrix.rust_target }} on nightly - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - rust_target: - # BSDs: could be tested with full system emulation - - i686-unknown-freebsd - - x86_64-unknown-freebsd - manifest: ['psm/Cargo.toml', 'Cargo.toml'] - timeout-minutes: 10 - steps: - - uses: actions/checkout@v2 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - profile: minimal - target: ${{ matrix.rust_target }} - default: true - - name: Build ${{ matrix.rust_target }} - uses: actions-rs/cargo@v1 - with: - command: build - args: --target ${{ matrix.rust_target }} --manifest-path=${{ matrix.manifest }} - - cross-build: - name: Cross-compile ${{ matrix.manifest }} to ${{ matrix.rust_target }} with cargo-cross - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - rust_target: - # https://github.com/rust-embedded/cross/issues/333 - - powerpc64le-unknown-linux-gnu - # FIXME: Testing hangs, should be verified once-in-a-while manually. - # could be made work by using full system emulation - # https://github.com/rust-embedded/cross/issues/242 - # - # Currently tested manually with full-system emulation. - - s390x-unknown-linux-gnu - # FIXME: tests could be made work by using full system emulation, maybe? - # - # Currently tested manually on real hardware. - # FIXME: https://github.com/rust-embedded/cross/pull/440 - # - sparc64-unknown-linux-gnu + exclude: + - mode: '-Zminimal-versions' + rust_toolchain: stable + include: # BSDs: could be tested with full system emulation - - x86_64-unknown-netbsd - manifest: ['psm/Cargo.toml', 'Cargo.toml'] + - rust_target: x86_64-unknown-netbsd + build_only: true + - rust_target: i686-unknown-freebsd + build_only: true + - rust_target: x86_64-unknown-freebsd + build_only: true timeout-minutes: 10 steps: - - uses: actions/checkout@v2 - - name: Install Rust nightly - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - profile: minimal - target: ${{ matrix.rust_target }} - default: true - - name: Build ${{ matrix.rust_target }} - uses: actions-rs/cargo@v1 - with: - use-cross: true - command: build - args: --target ${{ matrix.rust_target }} --manifest-path=${{ matrix.manifest }} + - uses: actions/checkout@v3 + - run: rustup install ${{ matrix.rust_toolchain }} --profile=minimal + - run: rustup default ${{ matrix.rust_toolchain }} + - run: rustup target add ${{ matrix.rust_target }} + - run: | + mkdir -p "${{ runner.tool_cache }}/cross" + curl --fail -L 'https://github.com/cross-rs/cross/releases/download/v0.2.4/cross-x86_64-unknown-linux-gnu.tar.gz' | tar xzf - -C "${{ runner.tool_cache }}/cross" + echo "${{ runner.tool_cache }}/cross" >> $GITHUB_PATH + - run: cross build --target ${{ matrix.rust_target }} --all ${{ matrix.mode }} + if: matrix.build_only == true + - run: cross test --target ${{ matrix.rust_target }} --all ${{ matrix.mode }} -- --test-threads=1 --nocapture + if: matrix.build_only != true cross-ios-build: - name: Cross-compile ${{ matrix.manifest }} to ${{ matrix.rust_target }} on ${{ matrix.rust_toolchain }} + name: "Cross: ${{ toJSON(matrix) }}" runs-on: macos-latest strategy: fail-fast: false @@ -240,51 +141,37 @@ jobs: rust_target: - aarch64-apple-ios - x86_64-apple-ios - manifest: ['psm/Cargo.toml', 'Cargo.toml'] timeout-minutes: 10 steps: - - uses: actions/checkout@v2 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.rust_toolchain }} - profile: minimal - target: ${{ matrix.rust_target }} - default: true - - name: Build - uses: actions-rs/cargo@v1 - with: - command: build - args: --target=${{ matrix.rust_target }} --manifest-path=${{ matrix.manifest }} + - uses: actions/checkout@v3 + - run: rustup install ${{ matrix.rust_toolchain }} --profile=minimal + - run: rustup default ${{ matrix.rust_toolchain }} + - run: rustup target add ${{ matrix.rust_target }} + - run: cargo build --target=${{ matrix.rust_target }} --all cross-windows-build: - name: Cross-compile ${{ matrix.manifest }} for ${{ matrix.rust_target }} from x86_64-unknown-linux-gnu + name: "Cross: ${{ toJSON(matrix) }}" runs-on: ubuntu-20.04 strategy: fail-fast: true matrix: + rust_toolchain: [stable] rust_target: - x86_64-pc-windows-msvc - i686-pc-windows-msvc - manifest: ['psm/Cargo.toml', 'Cargo.toml'] xwin_version: ["0.1.6"] timeout-minutes: 10 steps: - - uses: actions/checkout@v2 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - target: ${{ matrix.rust_target }} + - uses: actions/checkout@v3 + - run: rustup install ${{ matrix.rust_toolchain }} --profile=minimal + - run: rustup default ${{ matrix.rust_toolchain }} + - run: rustup target add ${{ matrix.rust_target }} - name: Add toolchain shims run: | - set -eux sudo ln -s clang-12 /usr/bin/clang-cl sudo ln -s llvm-ar-12 /usr/bin/llvm-lib sudo ln -s lld-link-12 /usr/bin/lld-link - - name: Install Windows SDK - run: | + - run: | set -eux xwin_version=${{ matrix.xwin_version }} xwin_prefix="xwin-$xwin_version-x86_64-unknown-linux-musl" @@ -294,7 +181,7 @@ jobs: # Splat the CRT and SDK files to /tmp/xwin/crt and /tmp/xwin/sdk respectively xwin --accept-license 1 splat --output /tmp/xwin - - name: Test + - run: cargo build --target ${{ matrix.rust_target }} --all env: CC: "clang-cl" CXX: "clang-cl" @@ -307,24 +194,22 @@ jobs: CFLAGS: "-Wno-unused-command-line-argument -fuse-ld=lld-link /imsvc/tmp/xwin/crt/include /imsvc/tmp/xwin/sdk/include/ucrt /imsvc/tmp/xwin/sdk/include/um /imsvc/tmp/xwin/sdk/include/shared" # Inform the linker where to search for libraries RUSTFLAGS: "-Lnative=/tmp/xwin/crt/lib/x86_64 -Lnative=/tmp/xwin/sdk/lib/um/x86_64 -Lnative=/tmp/xwin/sdk/lib/ucrt/x86_64" - run: | - set -eux - cargo build --target ${{ matrix.rust_target }} --manifest-path ${{ matrix.manifest }} - wasm-test: - name: Test stacker on WASM + test-wasm: + name: "Test: ${{ toJSON(matrix) }}" runs-on: ubuntu-latest + strategy: + matrix: + rust_toolchain: [stable] + rust_target: [wasm32-wasi] timeout-minutes: 10 steps: - - uses: actions/checkout@v1 - - name: Install Rust nightly - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - default: true - target: wasm32-wasi + - uses: actions/checkout@v3 + - run: rustup install ${{ matrix.rust_toolchain }} --profile=minimal + - run: rustup default ${{ matrix.rust_toolchain }} + - run: rustup target add ${{ matrix.rust_target }} - run: | curl -Lf https://github.com/bytecodealliance/wasmtime/releases/download/v0.19.0/wasmtime-v0.19.0-x86_64-linux.tar.xz | tar xJf - -C ${{ runner.tool_cache }} echo "${{ runner.tool_cache }}/wasmtime-v0.19.0-x86_64-linux" >> $GITHUB_PATH echo "CARGO_TARGET_WASM32_WASI_RUNNER=wasmtime run --" >> $GITHUB_ENV - - run: cargo test --target wasm32-wasi --all -- --nocapture + - run: cargo test --target ${{ matrix.rust_target }} --all -- --nocapture diff --git a/Cross.toml b/Cross.toml index 7e330ab..5df355a 100644 --- a/Cross.toml +++ b/Cross.toml @@ -1,2 +1,2 @@ -[target.x86_64-linux-android] -image = "rustembedded/cross:x86_64-linux-android" +[target.x86_64-unknown-freebsd.env] +passthrough = ["AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd12-ar"] From ed5888593aa0937844a1d8934147029dd5181001 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Tue, 1 Nov 2022 13:11:35 +0200 Subject: [PATCH 3/4] Move tests to a single file This avoids compiling a binary for each separate test. Makes it much easier to point a debugger at psm test cases too. --- psm/Cargo.toml | 6 + psm/examples/panics.rs | 52 ------- psm/examples/thread.rs | 60 --------- psm/src/lib.rs | 13 +- psm/src/tests.rs | 239 +++++++++++++++++++++++++++++++++ psm/tests/stack_direction.rs | 6 - psm/tests/stack_direction_2.rs | 29 ---- stacker/src/lib.rs | 7 +- 8 files changed, 261 insertions(+), 151 deletions(-) delete mode 100644 psm/examples/panics.rs delete mode 100644 psm/examples/thread.rs create mode 100644 psm/src/tests.rs delete mode 100644 psm/tests/stack_direction.rs delete mode 100644 psm/tests/stack_direction_2.rs diff --git a/psm/Cargo.toml b/psm/Cargo.toml index 9a3c56a..f8b79d1 100644 --- a/psm/Cargo.toml +++ b/psm/Cargo.toml @@ -10,7 +10,13 @@ repository = "https://github.com/rust-lang/stacker/" documentation = "https://docs.rs/psm/0.1.20" readme = "README.mkd" +[lib] +harness = false + [dependencies] [build-dependencies] cc = "1.0.2" + +[dev-dependencies] +backtrace = "0.3" diff --git a/psm/examples/panics.rs b/psm/examples/panics.rs deleted file mode 100644 index ada658d..0000000 --- a/psm/examples/panics.rs +++ /dev/null @@ -1,52 +0,0 @@ -extern 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"); - } - } -} diff --git a/psm/examples/thread.rs b/psm/examples/thread.rs deleted file mode 100644 index eb335a5..0000000 --- a/psm/examples/thread.rs +++ /dev/null @@ -1,60 +0,0 @@ -extern 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() -} diff --git a/psm/src/lib.rs b/psm/src/lib.rs index c83ca3a..61e494c 100644 --- a/psm/src/lib.rs +++ b/psm/src/lib.rs @@ -1,6 +1,17 @@ #![doc=include_str!("../README.mkd")] #![allow(unused_macros)] -#![no_std] +#![cfg_attr(not(test), no_std)] + +#[cfg(test)] +extern crate core; + +#[path = "tests.rs"] +mod tests; + +#[cfg(test)] +fn main() { + tests::run(); +} macro_rules! extern_item { (unsafe $($toks: tt)+) => { diff --git a/psm/src/tests.rs b/psm/src/tests.rs new file mode 100644 index 0000000..973f2b1 --- /dev/null +++ b/psm/src/tests.rs @@ -0,0 +1,239 @@ +#![cfg(test)] + +macro_rules! tests { + ($($(#[$meta:meta])* fn $name:ident() { $($body:tt)* })*) => { + $( + $(#[$meta])* + fn $name() { + $($body)* + } + + )* + + static TESTS: &[(&'static str, fn())] = &[$((stringify!($name), $name)),*]; + } +} + +fn ptr_distance(a: *const (), b: *const ()) -> usize { + (a as isize).wrapping_sub(b as isize).abs() as usize +} + +fn alloc_stack(size: usize) -> *mut u8 { + const STACK_ALIGN: usize = 4096; + unsafe { + let layout = std::alloc::Layout::from_size_align(size, STACK_ALIGN).unwrap(); + let new_stack = std::alloc::alloc(layout); + assert!(!new_stack.is_null(), "allocations must succeed!"); + new_stack + } +} + +#[no_mangle] +fn rust_psm_test_get_bt() -> backtrace::Backtrace { + backtrace::Backtrace::new() +} + +fn find_frame_name(bt: &backtrace::Backtrace, name: &[u8]) -> Option<(usize, usize)> { + for (frame_idx, frame) in bt.frames().into_iter().enumerate() { + for (symbol_idx, symbol) in frame.symbols().into_iter().enumerate() { + if symbol.name().map(|n| n.as_bytes()) == Some(name) { + return Some((frame_idx, symbol_idx)); + } + } + } + None +} + +tests! { + fn stack_direction_always_equal() { + assert_eq!(crate::StackDirection::new(), crate::StackDirection::new()); + } + + fn stack_direction_is_correct() { + #[inline(never)] + fn test_direction(previous_sp: *mut u8) { + let current_sp = crate::stack_pointer(); + match crate::StackDirection::new() { + crate::StackDirection::Ascending => { + assert!( + current_sp > previous_sp, + "the stack pointer is not ascending! current = {:p}, previous = {:p}", + current_sp, + previous_sp + ); + } + crate::StackDirection::Descending => { + assert!( + current_sp < previous_sp, + "the stack pointer is not descending! current = {:p}, previous = {:p}", + current_sp, + previous_sp + ); + } + } + } + test_direction(crate::stack_pointer()); + } + + fn basic_on_stack() { + unsafe { + let new_stack = alloc_stack(4096); + let r = crate::on_stack(new_stack, 4096, || (crate::stack_pointer(), 42 + 42_0000)); + assert_eq!(r.1, 42_0042); + assert!(ptr_distance(crate::stack_pointer() as _, r.0 as _) > 0x1000_0000); + assert!(ptr_distance(new_stack as _, r.0 as _) < 4096); + } + } + + #[inline(never)] + fn on_stack_basic_backtrace() { + let bt = unsafe { + let new_stack = alloc_stack(128 * 4096); + crate::on_stack(new_stack, 128 * 4096, rust_psm_test_get_bt) + }; + + let test_get_bt_frame = find_frame_name(&bt, b"rust_psm_test_get_bt"); + let on_stack_bt_frame = find_frame_name(&bt, b"rust_psm_on_stack"); + + assert!(test_get_bt_frame.is_some()); + assert!(on_stack_bt_frame.is_some()); + assert!(bt.frames().len() > on_stack_bt_frame.unwrap().0 + 4); + } + + + fn on_stack_panic_handling() { + use std::panic; + const CHAIN_DEPTH: usize = 16; + fn panic_chain(depth: usize) { + if depth == 0 { + panic!("full chain!"); + } else { + unsafe { + // 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... + let new_stack = alloc_stack(128 * 4096); + let p = crate::on_stack(new_stack, 128 * 4096, || { + panic::catch_unwind(|| { + panic_chain(depth - 1); + }) + }); + p.map_err(panic::resume_unwind).unwrap() + } + } + } + assert!(panic::catch_unwind(|| { + panic_chain(CHAIN_DEPTH); + }).is_err(), "Panic did not propagate!"); + } + + fn on_stack_multithread() { + use std::thread; + const FIB_COUNTS: [(usize, u64); 3] = [ + (8, 21), + (16, 987), + (24, 46368), + ]; + + #[inline(never)] + fn fib(n: usize) -> u64 { + unsafe { + let new_stack = alloc_stack(4096); + let r = match n { + 0 => 0, + 1 => 1, + _ => { + crate::on_stack(new_stack, 4096, || { + fib(n - 1) + fib(n - 2) + }) + } + }; + r + } + } + + for (expected, handle) in FIB_COUNTS.iter().map(|&(n, expected)| + (expected, thread::spawn(move || { + fib(n) + })) + ) { + if let Ok(res) = handle.join() { + assert_eq!(res, expected); + } else { + panic!("joining a thread returned an Err"); + } + } + } + + fn replace_stack_basic() { + unsafe { + let init_stack_ptr = crate::stack_pointer(); + let new_stack = alloc_stack(4096 * 64); + crate::replace_stack(new_stack, 4096 * 64, || { + assert!(ptr_distance(crate::stack_pointer() as _, init_stack_ptr as _) > 0x1000_0000); + assert!(ptr_distance(crate::stack_pointer() as _, new_stack as _) < 4096 * 64); + std::process::exit(0) + }); + } + } + + fn replace_stack_panic() { + unsafe { + let new_stack = alloc_stack(4096 * 64); + crate::replace_stack(new_stack, 4096 * 64, || { + let unwind = std::panic::catch_unwind(|| panic!("test") ); + assert!(unwind.is_err()); + std::process::exit(0); + }); + } + } + + fn replace_stack_backtrace() { + unsafe { + let new_stack = alloc_stack(128 * 4096); + crate::replace_stack(new_stack, 128 * 4096, || { + let bt = rust_psm_test_get_bt(); + let test_get_bt_frame = find_frame_name(&bt, b"rust_psm_test_get_bt"); + assert!(test_get_bt_frame.is_some()); + assert!(bt.frames().len() < test_get_bt_frame.unwrap().0 + 8); + std::process::exit(0); + }) + } + } +} + +#[inline(never)] +pub(crate) fn run() { + let this = std::env::args_os().next().unwrap(); + if let Some(test_to_run) = std::env::args_os().nth(1) { + for (name, test) in TESTS { + if test_to_run == std::ffi::OsStr::new(name) { + test(); + std::process::exit(0); + } + } + } else { + let mut failures = 0; + for (name, _) in TESTS { + let mut cmd = std::process::Command::new(&this); + cmd.arg(name); + eprintln!("{:?}...", cmd); + failures = failures + + match cmd.status() { + Ok(s) if s.success() => continue, + Ok(s) => { + eprintln!("test did not complete successfully: {:?}", s); + 1 + } + Err(e) => { + eprintln!("could not spawn self for single test: {}", e); + 1 + } + } + } + if failures != 0 { + eprintln!("{} tests failed", failures); + std::process::exit(failures); + } + } +} diff --git a/psm/tests/stack_direction.rs b/psm/tests/stack_direction.rs deleted file mode 100644 index 609decb..0000000 --- a/psm/tests/stack_direction.rs +++ /dev/null @@ -1,6 +0,0 @@ -extern crate psm; - -#[test] -fn always_equal() { - assert_eq!(psm::StackDirection::new(), psm::StackDirection::new()); -} diff --git a/psm/tests/stack_direction_2.rs b/psm/tests/stack_direction_2.rs deleted file mode 100644 index dd06790..0000000 --- a/psm/tests/stack_direction_2.rs +++ /dev/null @@ -1,29 +0,0 @@ -extern 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()); -} diff --git a/stacker/src/lib.rs b/stacker/src/lib.rs index 505293d..a7a6536 100644 --- a/stacker/src/lib.rs +++ b/stacker/src/lib.rs @@ -35,9 +35,10 @@ pub fn maybe_grow R>(red_zone: usize, stack_size: usize, callb } } -/// Always creates a new stack for the passed closure to run on. -/// The closure will still be on the same thread as the caller of `grow`. -/// This will allocate a new stack with at least `stack_size` bytes. +/// Always runs the passed closure on a new stack. +/// +/// The closure will still execute on the same thread as the caller of `grow`. +/// This will allocate a new stack of at least `stack_size` bytes. pub fn grow R>(stack_size: usize, callback: F) -> R { // To avoid monomorphizing `_grow()` and everything it calls, // we convert the generic callback to a dynamic one. From ae88757a6ea28ed85040a1af3e551daf6cb6b22a Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Tue, 1 Nov 2022 21:20:06 +0200 Subject: [PATCH 4/4] Migrate archs that support inline assembly to Rust MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This really has a couple benefits – first, is that we have an opportunity to simplify the code in some instances. But most importantly, we no longer need to deal with external assemblers and different syntax for each. Eventually we might even be able to utilize them macros to reduce duplication too! --- psm/Cargo.toml | 1 + psm/build.rs | 34 ++++--------- psm/src/arch/aarch64.rs | 70 ++++++++++++++++++++++++++ psm/src/arch/arm.rs | 71 ++++++++++++++++++++++++++ psm/src/arch/mod.rs | 27 ++++++++++ psm/src/arch/x86.rs | 63 +++++++++++++++++++++++ psm/src/arch/x86_64.rs | 63 +++++++++++++++++++++++ psm/src/arch/x86_64_msvc.asm | 46 ++++++++++++----- psm/src/arch/x86_64_windows.rs | 92 ++++++++++++++++++++++++++++++++++ psm/src/arch/x86_windows.rs | 63 +++++++++++++++++++++++ psm/src/lib.rs | 40 ++++++++------- psm/src/tests.rs | 10 ++-- 12 files changed, 521 insertions(+), 59 deletions(-) create mode 100644 psm/src/arch/aarch64.rs create mode 100644 psm/src/arch/arm.rs create mode 100644 psm/src/arch/mod.rs create mode 100644 psm/src/arch/x86.rs create mode 100644 psm/src/arch/x86_64.rs create mode 100644 psm/src/arch/x86_64_windows.rs create mode 100644 psm/src/arch/x86_windows.rs diff --git a/psm/Cargo.toml b/psm/Cargo.toml index f8b79d1..c5a1a30 100644 --- a/psm/Cargo.toml +++ b/psm/Cargo.toml @@ -14,6 +14,7 @@ readme = "README.mkd" harness = false [dependencies] +cfg-if = "1.0.0" [build-dependencies] cc = "1.0.2" diff --git a/psm/build.rs b/psm/build.rs index 9d40212..76dc313 100644 --- a/psm/build.rs +++ b/psm/build.rs @@ -12,30 +12,16 @@ fn find_assembly( // 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)), + + // ("x86", _, "windows", _) if masm => Some(("src/arch/x86_msvc.asm", true)), + // ("x86", _, "windows", _) => Some(("src/arch/x86_windows_gnu.s", true)), + // ("x86_64", _, "windows", _) if masm => Some(("src/arch/x86_64_msvc.asm", true)), + // ("x86_64", _, "windows", _) => Some(("src/arch/x86_64_windows_gnu.s", true)), + // ("arm", _, "windows", "msvc") => Some(("src/arch/arm_armasm.asm", true)), + ("aarch64", _, "windows", _) if masm => Some(("src/arch/aarch64_armasm.asm", true)), + ("aarch64", _, "windows", _) => Some(("src/arch/aarch_aapcs64.s", true)), + // ("x86", _, _, _) => Some(("src/arch/x86.s", false)), + // ("x86_64", _, _, _) => Some(("src/arch/x86_64.s", false)), ("arm", _, _, _) => Some(("src/arch/arm_aapcs.s", true)), ("aarch64", _, _, _) => Some(("src/arch/aarch_aapcs64.s", true)), ("powerpc", _, _, _) => Some(("src/arch/powerpc32.s", true)), diff --git a/psm/src/arch/aarch64.rs b/psm/src/arch/aarch64.rs new file mode 100644 index 0000000..65e27dc --- /dev/null +++ b/psm/src/arch/aarch64.rs @@ -0,0 +1,70 @@ +pub(crate) fn stack_direction() -> crate::StackDirection { + crate::StackDirection::Descending +} + +pub(crate) fn stack_pointer() -> *mut u8 { + let mut ret; + unsafe { + core::arch::asm! { + "mov {ret}, sp", + ret = lateout(reg) ret, + options(preserves_flags, nomem), + } + } + ret +} + +pub(crate) unsafe extern "C" fn replace_stack( + data: usize, + callback: unsafe extern "C" fn(usize) -> !, + sp: *mut u8, + _: *mut u8, +) -> ! { + core::arch::asm! { + "mov sp, {new_sp}", + "br {callback}", + "ud2", + new_sp = in(reg) sp, + callback = in(reg) callback, + in("r0") data, + options(noreturn, nostack), + } +} + +core::arch::global_asm! { + ".balign 8", + ".local rust_psm_on_stack", + ".hidden rust_psm_on_stack", + ".type rust_psm_on_stack,@function", + "rust_psm_on_stack:", + ".cfi_startproc", + "stp fp, lr, [sp, #-16]!" + ".cfi_offset lr, -8", + ".cfi_offset fp, -16", + "mov fp, sp", + ".cfi_def_cfa_register fp", + "mov sp, x3", + "blr x2", + "mov sp, fp", + ".cfi_def_cfa_register sp", + "ldp fp, lr, [fp], #16", + "ret", + ".cfi_endproc", +} + +pub(crate) unsafe extern "C" fn on_stack( + data: usize, + return_ptr: usize, + callback: unsafe extern "C" fn(usize, usize), + sp: *mut u8, + _: *mut u8, +) { + core::arch::asm! { + "call rust_psm_on_stack", + in("x0") data, + in("x1") return_ptr, + in("x2") callback, + in("x3") sp, + clobber_abi("C"), + } +} diff --git a/psm/src/arch/arm.rs b/psm/src/arch/arm.rs new file mode 100644 index 0000000..1782f22 --- /dev/null +++ b/psm/src/arch/arm.rs @@ -0,0 +1,71 @@ +pub(crate) fn stack_direction() -> crate::StackDirection { + crate::StackDirection::Descending +} + +pub(crate) fn stack_pointer() -> *mut u8 { + let mut ret; + unsafe { + core::arch::asm! { + "mov {ret}, sp", + ret = lateout(reg) ret, + options(preserves_flags, nomem), + } + } + ret +} + +pub(crate) unsafe extern "aapcs" fn replace_stack( + data: usize, + callback: unsafe extern "aapcs" fn(usize) -> !, + sp: *mut u8, + _: *mut u8, +) -> ! { + core::arch::asm! { + "mov sp, {new_sp}", + "bx {callback}", + "ud2", + new_sp = in(reg) sp, + callback = in(reg) callback, + in("r0") data, + options(noreturn, nostack), + } +} + +core::arch::global_asm! { + ".balign 8", + ".local rust_psm_on_stack", + ".hidden rust_psm_on_stack", + ".type rust_psm_on_stack,@function", + "rust_psm_on_stack:", + ".fnstart", + ".cfi_startproc", + "push {fp, lr}", + ".cfi_def_cfa_offset 8", + ".cfi_offset lr, -4", + ".cfi_offset fp, -8", + "mov fp, sp", + ".cfi_def_cfa_register fp", + "mov sp, r3", + "blx r2", + "mov sp, fp", + "pop {fp, pc}", + ".cfi_endproc", + ".fnend", +} + +pub(crate) unsafe extern "aapcs" fn on_stack( + data: usize, + return_ptr: usize, + callback: unsafe extern "aapcs" fn(usize, usize), + sp: *mut u8, + _: *mut u8, +) { + core::arch::asm! { + "call rust_psm_on_stack", + in("r0") data, + in("r1") return_ptr, + in("r2") callback, + in("r3") sp, + clobber_abi("aapcs"), + } +} diff --git a/psm/src/arch/mod.rs b/psm/src/arch/mod.rs new file mode 100644 index 0000000..6d2580d --- /dev/null +++ b/psm/src/arch/mod.rs @@ -0,0 +1,27 @@ +cfg_if::cfg_if! { + if #[cfg(asm)] { + compile_error!("not implemented yet"); + } else if #[cfg(all(target_arch="x86", target_os="windows"))] { + #[path = "x86_windows.rs"] + mod imp; + } else if #[cfg(target_arch="x86")] { + #[path = "x86.rs"] + mod imp; + } else if #[cfg(all(target_arch="x86_64", target_os="windows"))] { + #[path = "x86_64_windows.rs"] + mod imp; + } else if #[cfg(target_arch="x86_64")] { + #[path = "x86_64.rs"] + mod imp; + } else if #[cfg(target_arch="arm")] { + #[path = "arm.rs"] + mod imp; + } else if #[cfg(target_arch="aarch64")] { + #[path = "aarch64.rs"] + mod imp; + } else { + compile_error!("Target is not supported by the `psm` crate!"); + } +} + +pub(crate) use self::imp::*; diff --git a/psm/src/arch/x86.rs b/psm/src/arch/x86.rs new file mode 100644 index 0000000..4617b57 --- /dev/null +++ b/psm/src/arch/x86.rs @@ -0,0 +1,63 @@ +pub(crate) fn stack_direction() -> crate::StackDirection { + crate::StackDirection::Descending +} + +pub(crate) fn stack_pointer() -> *mut u8 { + let mut ret; + unsafe { + core::arch::asm! { + "mov {ret}, esp", + ret = lateout(reg) ret, + options(preserves_flags, nomem), + } + } + ret +} + +pub(crate) unsafe fn replace_stack( + data: usize, + callback: unsafe extern "fastcall" fn(usize) -> !, + sp: *mut u8, + _: *mut u8, +) -> ! { + core::arch::asm! { + "lea esp, [{sp} - 12]", + "jmp {callback}", + sp = in(reg) sp, + callback = in(reg) callback, + in("ecx") data, + options(noreturn, nostack), + } +} + +core::arch::global_asm! { + ".balign 16", + ".local rust_psm_on_stack", + ".hidden rust_psm_on_stack", + ".type rust_psm_on_stack,@function", + "rust_psm_on_stack:", + ".cfi_startproc", + "xchg esp, edi", + ".cfi_def_cfa_register edi", + "call eax", + "mov esp, edi", + "ret", + ".cfi_endproc", +} + +pub(crate) unsafe fn on_stack( + data: usize, + return_ptr: usize, + callback: unsafe extern "fastcall" fn(usize, usize), + sp: *mut u8, + _: *mut u8, +) { + core::arch::asm! { + "call rust_psm_on_stack", + in("ecx") data, + in("edx") return_ptr, + in("eax") callback, + inout("edi") sp => _, + clobber_abi("fastcall"), + } +} diff --git a/psm/src/arch/x86_64.rs b/psm/src/arch/x86_64.rs new file mode 100644 index 0000000..913baa4 --- /dev/null +++ b/psm/src/arch/x86_64.rs @@ -0,0 +1,63 @@ +pub(crate) fn stack_direction() -> crate::StackDirection { + crate::StackDirection::Descending +} + +pub(crate) fn stack_pointer() -> *mut u8 { + let mut ret; + unsafe { + core::arch::asm! { + "mov {ret}, rsp", + ret = lateout(reg) ret, + options(preserves_flags, nomem), + } + } + ret +} + +pub(crate) unsafe fn replace_stack( + data: usize, + callback: unsafe extern "sysv64" fn(usize) -> !, + sp: *mut u8, + _: *mut u8, +) -> ! { + core::arch::asm! { + "lea rsp, [{sp} - 8]", + "jmp {callback}", + sp = in(reg) sp, + callback = in(reg) callback, + in("rdi") data, + options(noreturn, nostack), + } +} + +core::arch::global_asm! { + ".balign 16", + ".local rust_psm_on_stack", + ".hidden rust_psm_on_stack", + ".type rust_psm_on_stack,@function", + "rust_psm_on_stack:", + ".cfi_startproc", + "xchg rsp, r12", + ".cfi_def_cfa_register r12", + "call rdx", + "mov rsp, r12", + "ret", + ".cfi_endproc", +} + +pub(crate) unsafe fn on_stack( + data: usize, + return_ptr: usize, + callback: unsafe extern "sysv64" fn(usize, usize), + sp: *mut u8, + _: *mut u8, +) { + core::arch::asm! { + "call rust_psm_on_stack", + in("rdi") data, + in("rsi") return_ptr, + in("rdx") callback, + inout("r12") sp => _, + clobber_abi("sysv64"), + } +} diff --git a/psm/src/arch/x86_64_msvc.asm b/psm/src/arch/x86_64_msvc.asm index 67d7283..c7b5fc8 100644 --- a/psm/src/arch/x86_64_msvc.asm +++ b/psm/src/arch/x86_64_msvc.asm @@ -19,10 +19,15 @@ 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 + mov qword ptr gs:[00h], -1 + mov qword ptr gs:[10h], rcx + mov qword ptr gs:[08h], rdx + mov qword ptr gs:[1478h], rcx + mov qword ptr gs:[1748h], 0 + lea rsp, [rdx - 8] jmp rsi + ud2 rust_psm_replace_stack ENDP ; extern "sysv64" fn(%rdi: usize, %rsi: usize, @@ -34,25 +39,42 @@ rust_psm_replace_stack ENDP ; ; 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. +; FIXME: this needs a catch-all exception handler that aborts in case anything unwinds into here. rust_psm_on_stack PROC FRAME - push rbp + push qword ptr rbp .pushreg rbp + push qword ptr gs:[1748h] ; GuaranteedStackBytes + .allocstack 8 + push qword ptr gs:[1478h] ; DeallocationStack + .allocstack 8 + push qword ptr gs:[10h] ; StackLimit + .allocstack 8 + push qword ptr gs:[08h] ; StackBase + .allocstack 8 + push qword ptr gs:[00h] ; ExceptionList + .allocstack 8 mov rbp, rsp .setframe rbp, 0 .endprolog - push gs:[08h] - mov gs:[08h], rcx - push gs:[10h] - mov gs:[10h], r8 mov rsp, rcx + + mov qword ptr gs:[00h], -1 + mov qword ptr gs:[08h], rcx + mov qword ptr gs:[10h], r8 + ; TODO: these need a more proper handling + mov qword ptr gs:[1478h], rcx + mov qword ptr gs:[1748h], 0 + call rdx - lea rsp, [rbp - 010h] - pop gs:[10h] - pop gs:[08h] - pop rbp + mov rsp, rbp + pop qword ptr gs:[00h] + pop qword ptr gs:[08h] + pop qword ptr gs:[10h] + pop qword ptr gs:[1478h] + pop qword ptr gs:[1748h] + pop qword ptr rbp ret rust_psm_on_stack ENDP diff --git a/psm/src/arch/x86_64_windows.rs b/psm/src/arch/x86_64_windows.rs new file mode 100644 index 0000000..cae4a8c --- /dev/null +++ b/psm/src/arch/x86_64_windows.rs @@ -0,0 +1,92 @@ +pub(crate) fn stack_direction() -> crate::StackDirection { + crate::StackDirection::Descending +} + +pub(crate) fn stack_pointer() -> *mut u8 { + let mut ret; + unsafe { + core::arch::asm! { + "mov {ret}, rsp", + ret = lateout(reg) ret, + options(preserves_flags, nomem), + } + } + ret +} + +pub(crate) unsafe fn replace_stack( + data: usize, + callback: unsafe extern "sysv64" fn(usize) -> !, + sp: *mut u8, + _: *mut u8, +) -> ! { + core::arch::asm! { + "mov qword ptr gs:[0x0], -1", + "mov qword ptr gs:[0x8], rdx", + "mov qword ptr gs:[0x10], rcx", + "mov qword ptr gs:[0x1478], rcx", + "mov qword ptr gs:[0x1748], 0", + "lea rsp, [{sp} - 8]", + "jmp {callback}", + sp = in(reg) sp, + callback = in(reg) callback, + in("rdi") data, + options(noreturn, nostack), + } +} + +core::arch::global_asm! { + ".balign 16", + "rust_psm_on_stack:", + ".seh_proc rust_psm_on_stack", + + "push qword ptr gs:[0x1748]", // GuaranteedStackBytes + ".seh_stackalloc 8", + "push qword ptr gs:[0x1478]", // DeallocationStack + ".seh_stackalloc 8", + "push qword ptr gs:[0x10]", // StackLimit + ".seh_stackalloc 8", + "push qword ptr gs:[0x08]", // StackBase + ".seh_stackalloc 8", + "push qword ptr gs:[0x00]", // ExceptionList + ".seh_stackalloc 8", + "xchg rsp, r12", + ".seh_setframe r12, 0", + ".seh_endprologue", + + "mov qword ptr gs:[0x00], -1", + "mov qword ptr gs:[0x08], rsp", + "mov qword ptr gs:[0x10], r8", + // TODO: these need a more proper handling + "mov qword ptr gs:[0x1478], rsp", + "mov qword ptr gs:[0x1748], 0", + + "call rdx", + + // Reset the state. + "mov rsp, r12", + "pop qword ptr gs:[0x00]", + "pop qword ptr gs:[0x08]", + "pop qword ptr gs:[0x10]", + "pop qword ptr gs:[0x1478]", + "pop qword ptr gs:[0x1748]", + "ret", + ".seh_endproc", +} + +pub(crate) unsafe fn on_stack( + data: usize, + return_ptr: usize, + callback: unsafe extern "sysv64" fn(usize, usize), + sp: *mut u8, + _: *mut u8, +) { + core::arch::asm! { + "call rust_psm_on_stack", + in("rdi") data, + in("rsi") return_ptr, + in("rdx") callback, + inout("r12") sp => _, + clobber_abi("sysv64"), + } +} diff --git a/psm/src/arch/x86_windows.rs b/psm/src/arch/x86_windows.rs new file mode 100644 index 0000000..4617b57 --- /dev/null +++ b/psm/src/arch/x86_windows.rs @@ -0,0 +1,63 @@ +pub(crate) fn stack_direction() -> crate::StackDirection { + crate::StackDirection::Descending +} + +pub(crate) fn stack_pointer() -> *mut u8 { + let mut ret; + unsafe { + core::arch::asm! { + "mov {ret}, esp", + ret = lateout(reg) ret, + options(preserves_flags, nomem), + } + } + ret +} + +pub(crate) unsafe fn replace_stack( + data: usize, + callback: unsafe extern "fastcall" fn(usize) -> !, + sp: *mut u8, + _: *mut u8, +) -> ! { + core::arch::asm! { + "lea esp, [{sp} - 12]", + "jmp {callback}", + sp = in(reg) sp, + callback = in(reg) callback, + in("ecx") data, + options(noreturn, nostack), + } +} + +core::arch::global_asm! { + ".balign 16", + ".local rust_psm_on_stack", + ".hidden rust_psm_on_stack", + ".type rust_psm_on_stack,@function", + "rust_psm_on_stack:", + ".cfi_startproc", + "xchg esp, edi", + ".cfi_def_cfa_register edi", + "call eax", + "mov esp, edi", + "ret", + ".cfi_endproc", +} + +pub(crate) unsafe fn on_stack( + data: usize, + return_ptr: usize, + callback: unsafe extern "fastcall" fn(usize, usize), + sp: *mut u8, + _: *mut u8, +) { + core::arch::asm! { + "call rust_psm_on_stack", + in("ecx") data, + in("edx") return_ptr, + in("eax") callback, + inout("edi") sp => _, + clobber_abi("fastcall"), + } +} diff --git a/psm/src/lib.rs b/psm/src/lib.rs index 61e494c..27b91fb 100644 --- a/psm/src/lib.rs +++ b/psm/src/lib.rs @@ -13,6 +13,8 @@ fn main() { tests::run(); } +mod arch; + macro_rules! extern_item { (unsafe $($toks: tt)+) => { unsafe extern "C" $($toks)+ @@ -178,7 +180,6 @@ unsafe fn rust_psm_on_stack( /// 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; @@ -197,13 +198,20 @@ pub unsafe fn on_stack R>(base: *mut u8, size: usize, callback }; let mut callback: MaybeUninit = MaybeUninit::new(callback); let mut return_value: MaybeUninit = MaybeUninit::uninit(); - rust_psm_on_stack( + arch::on_stack( &mut callback as *mut MaybeUninit as usize, &mut return_value as *mut MaybeUninit as usize, with_on_stack::, sp, base, ); + // 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(); } @@ -250,7 +258,6 @@ pub unsafe fn on_stack R>(base: *mut u8, size: usize, callback /// /// `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 @@ -262,12 +269,12 @@ pub unsafe fn replace_stack(base: *mut u8, size: usize, callback: F StackDirection::Ascending => base, StackDirection::Descending => base.offset(size as isize), }; - rust_psm_replace_stack( + arch::replace_stack( &callback as *const F as usize, with_replaced_stack::, sp, base, - ); + ) } /// The direction into which stack grows as stack frames are made. @@ -282,17 +289,17 @@ pub enum StackDirection { 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(), - } - } + arch::stack_direction() + // 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(), + // } + // } } } @@ -315,9 +322,8 @@ impl StackDirection { /// 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() } + arch::stack_pointer() } /// Macro that outputs its tokens only if `psm::on_stack` and `psm::replace_stack` are available. diff --git a/psm/src/tests.rs b/psm/src/tests.rs index 973f2b1..3085c92 100644 --- a/psm/src/tests.rs +++ b/psm/src/tests.rs @@ -29,6 +29,7 @@ fn alloc_stack(size: usize) -> *mut u8 { } #[no_mangle] +#[inline(never)] fn rust_psm_test_get_bt() -> backtrace::Backtrace { backtrace::Backtrace::new() } @@ -80,7 +81,7 @@ tests! { let new_stack = alloc_stack(4096); let r = crate::on_stack(new_stack, 4096, || (crate::stack_pointer(), 42 + 42_0000)); assert_eq!(r.1, 42_0042); - assert!(ptr_distance(crate::stack_pointer() as _, r.0 as _) > 0x1000_0000); + assert!(ptr_distance(crate::stack_pointer() as _, r.0 as _) > 0x100_0000); assert!(ptr_distance(new_stack as _, r.0 as _) < 4096); } } @@ -93,11 +94,8 @@ tests! { }; let test_get_bt_frame = find_frame_name(&bt, b"rust_psm_test_get_bt"); - let on_stack_bt_frame = find_frame_name(&bt, b"rust_psm_on_stack"); - assert!(test_get_bt_frame.is_some()); - assert!(on_stack_bt_frame.is_some()); - assert!(bt.frames().len() > on_stack_bt_frame.unwrap().0 + 4); + assert!(bt.frames().len() > test_get_bt_frame.unwrap().0 + 4); } @@ -170,7 +168,7 @@ tests! { let init_stack_ptr = crate::stack_pointer(); let new_stack = alloc_stack(4096 * 64); crate::replace_stack(new_stack, 4096 * 64, || { - assert!(ptr_distance(crate::stack_pointer() as _, init_stack_ptr as _) > 0x1000_0000); + assert!(ptr_distance(crate::stack_pointer() as _, init_stack_ptr as _) > 0x100_0000); assert!(ptr_distance(crate::stack_pointer() as _, new_stack as _) < 4096 * 64); std::process::exit(0) });
TargetSupportTarget Support
Architecture