diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..724b838 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,96 @@ +# Copilot Instructions for stackman + +## Overview +Low-level C library for stack manipulation (continuations/co-routines). ~600 lines of C + assembly. Zero dependencies. Platforms: Linux (x86/x64/ARM), Windows (x86/x64/ARM). Toolchains: GCC, Clang, MSVC. + +## Build Commands (FAST: <1 second) + +**Always run in order:** +```bash +make clean # Clean build artifacts +make all # Build library (0.1s) → lib/[ABI]/libstackman.a +make test # Build + run 4 test suites (0.7s) +make abiname # Print platform ABI (e.g., sysv_amd64) +``` + +**Success output:** `*** All test suites passed ***` + +**Cross-compile x86:** `make PLATFORMFLAGS=-m32 test` +**Cross-compile ARM:** `make PLATFORM_PREFIX=arm-linux-gnueabi- EMULATOR=qemu-arm test` +**Windows:** `msbuild vs2022\stackman.sln /p:Platform=x64` then `vs2022\x64\Debug\test.exe` + +## Critical Build Notes + +1. **Intel CET:** `-fcf-protection=none` flag REQUIRED (auto-added by disable_cet script). Stack switching incompatible with Shadow Stack. +2. **Libraries ARE Committed:** `lib/**/*.a` and `lib/**/*.lib` are version controlled (unlike typical projects). CI rebuilds and commits them. +3. **Expected Warning:** Linker warning "missing .note.GNU-stack section" in test_asm is NORMAL - ignore it. +4. **Artifacts:** `*.o`, `bin/`, `tmp/` NOT committed. Libraries in `lib/[ABI]/` ARE committed. +5. **Incremental OK:** After code changes, just `make test`. Only clean when switching platforms. + +## Project Structure + +**Key Directories:** +- `stackman/` - Main source: `stackman.h` (API), `stackman_switch.h`, `stackman_impl.h`, `platforms/` (15+ platform files) +- `tests/` - 4 test files: `test.c` (6 tests), `test_cc.cc`, `test_static.c`, `test_asm.c/.S` +- `lib/[ABI]/` - Pre-built libraries (COMMITTED to git) +- `vs2017/`, `vs2019/`, `vs2022/` - Visual Studio projects +- `tools/` - `abiname.sh`, `strip-lib.py` + +**Core API (2 functions only):** +```c +void *stackman_switch(stackman_cb_t callback, void *context); // Main stack switch +void *stackman_call(stackman_cb_t callback, void *context, void *stack); // Call with different stack +``` + +**Architecture:** `platforms/platform.h` detects OS/arch/compiler → includes appropriate `switch_[abi]_[compiler].h/S/asm` + +## CI Validation (.github/workflows/buildcommit.yml) + +**Triggers:** Push to master/dev, PRs to master + +**Jobs:** +1. **build-linux-gnu** (AMD64, i386, arm, aarch64) - installs cross-tools → `make all` → `make test` (qemu for ARM) +2. **build-windows** (x86, x64, arm, arm64) - MSBuild → strip-lib.py → rebuild (MUST rebuild after strip!) +3. **commit-artifacts** (push only) - downloads artifacts → commits libs → pushes + +**Local validation:** +```bash +make clean && make test # Test native platform +git status # Verify no bin/, tmp/, *.o tracked +``` + +## Key Patterns & Workarounds + +**Making Changes:** +- Platform code: edit `stackman/platforms/switch_*.h` or `.S` - reference `switch_template.h` +- Always run `make test` after changes (fast: 0.7s) +- Test on actual hardware if modifying assembly (arch-specific!) + +**Known Issues/Workarounds:** +- **CET:** `-fcf-protection=none` REQUIRED (auto-added by disable_cet script) +- **Inline asm:** May be inlined by optimizer → use separate .S files or volatile pointer (see stackman_impl.h) +- **Stack align:** Use `STACKMAN_SP_ALIGN` macro + +**Testing:** 4 test executables, 6 tests each (assertions fail hard). Success: "test_XX ok" + "*** All test suites passed ***" + +**Include patterns:** +- User code: `#include "stackman.h"` +- Library impl: `#include "stackman_impl.h"` (once) + +## Configuration Files +- **Build:** Makefile (Linux), vs2022/*.vcxproj (Windows) +- **CI:** .github/workflows/buildcommit.yml +- **Linting:** None configured +- **Testing:** `make test` target +- **.gitignore:** Excludes *.o, bin/, tmp/ BUT includes lib/**/*.a, lib/**/*.lib + +## Development Tips + +1. **Trust these instructions first** - search only if info incomplete/incorrect +2. **Build is FAST** - rebuild freely (clean+test <1s) +3. **Test after every change** - `make test` is fast and comprehensive +4. **Cross-compilation optional** - CI validates all platforms, native x64 sufficient for most changes +5. **Binary files in git** - lib/**/*.a, lib/**/*.lib ARE tracked (expect binary diffs) +6. **Zero dependencies** - don't add any +7. **Minimal changes** - stable library, surgical edits only +8. **Low-level code** - assembly is platform-specific, test on actual hardware diff --git a/.github/workflows/buildcommit.yml b/.github/workflows/buildcommit.yml index d858b1f..6d522ab 100644 --- a/.github/workflows/buildcommit.yml +++ b/.github/workflows/buildcommit.yml @@ -55,9 +55,8 @@ jobs: build-windows: runs-on: windows-latest strategy: - fail-fast: true matrix: - platform: [x86, x64, arm, arm64] + platform: [x86, x64, arm64] include: - platform: x86 folder: Win32 @@ -65,8 +64,6 @@ jobs: - platform: x64 folder: x64 native: yes - - platform: arm - folder: arm - platform: arm64 folder: arm64 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..17493e0 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,46 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.0] - 2025-11-16 + +### Added +- Version macros in `stackman.h`: `STACKMAN_VERSION_MAJOR`, `STACKMAN_VERSION_MINOR`, `STACKMAN_VERSION_PATCH`, `STACKMAN_VERSION`, `STACKMAN_VERSION_NUMBER` +- Automated release workflow for tagged versions + +### Changed +- Updated GitHub Actions to v4 (from v2) +- README updated with complete platform list, CI information, and release documentation + +### Removed +- **BREAKING**: Dropped Windows ARM32 (win_arm) support + - Microsoft Windows SDK 10.0.26100.0+ no longer supports 32-bit ARM development + - Last SDK version supporting ARM32 was Windows SDK 10.0.22621 (Windows 11 SDK, version 22H2) + - ARM32 Windows devices (Windows RT) are obsolete + - ARM64 Windows remains fully supported + +### Fixed +- Fixed typos in documentation and source files +- Corrected "callee-stored" → "callee-saved" terminology + +## [0.1] - 2020-05-18 + +### Added +- Core stack manipulation API: `stackman_switch()` and `stackman_call()` +- Support for 8 platforms: + - Linux: sysv_amd64, sysv_i386, arm32 (AAPCS), aarch64 (AAPCS64) + - Windows: win_x86, win_x64, win_arm (32-bit ARM), win_arm64 +- Compiler support: GCC, Clang, MSVC (VS2017, VS2019, VS2022) +- Pre-built libraries for all supported platforms +- Inline assembly and separate assembly file options +- Cross-compilation support for Linux (x86, ARM32, ARM64) +- QEMU-based testing for ARM platforms in CI +- Comprehensive test suite (test.c, test_cc.cc, test_static.c, test_asm.c) +- GitHub Actions CI for automated building and testing +- Visual Studio project files (VS2017, VS2019, VS2022) + +[1.0.0]: https://github.com/stackless-dev/stackman/releases/tag/v1.0.0 +[0.1]: https://github.com/stackless-dev/stackman/releases/tag/v0.1 diff --git a/README.md b/README.md index 36e4c46..c1e9d7b 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ # stackman +**Version 1.0.0** + Simple low-level stack manipulation API and implementation for common platforms ## Purpose @@ -75,17 +77,19 @@ calling convention, plus archive format: - win_x86 (32 bits) - win_x64 - - win_ARM64 (experimental) + - win_arm64 (64 bit ARM) - sysv_i386 (linux) - sysv_amd64 (linux) - - AAPCS (32 bit arm) - - AAPCS64 (64 bit arm) + - AAPCS (32 bit arm - linux) + - AAPCS64 (64 bit arm - linux) + +All platforms are automatically built and tested by GitHub Actions CI on every commit. ### Supported toolchains: - Gnu C - clang - - Microsoft Visual Studio + - Microsoft Visual Studio (VS2017, VS2019, VS2022) Other platforms can be easily adapted from both existing implementations for other projects as well as from example code provided. @@ -150,21 +154,33 @@ There are two basic ways to add the library to your project: Using a static libr ### static library (preferred) - - You link with the `libstackman.a` or `stackman.lib` libraries provided for your platform. + - Link with the `libstackman.a` or `stackman.lib` libraries provided for your platform in the `lib/` directory. + - Pre-built libraries are available for all supported platforms (8 ABIs total). + - Libraries are automatically rebuilt by CI and committed to the repository for easy integration. ### inlined code - - You inlude `stackman_impl.h` in one of your .c source files to provide inline assembly. - - You include `stackman_impl.h` in an assembly (.S) file in your project to include assembly code. - - (windows) You include `stackman_s.asm` in an assemby (.asm) file in your project. - In the case of inlined code, it can be specified to prefer in-line assembly and static linkage - over separate assembly language source. + - Include `stackman_impl.h` in one of your .c source files to provide inline assembly. + - Include `stackman_impl.h` in an assembly (.S) file in your project to include assembly code. + - (Windows) Include `stackman_s.asm` in an assembly (.asm) file in your project. + +In the case of inlined code, it can be specified to prefer in-line assembly and static linkage +over separate assembly language source. + +## Continuous Integration + +The project uses GitHub Actions to automatically: +- Build libraries for all 8 supported platforms (Linux: AMD64, i386, ARM32, ARM64; Windows: x86, x64, ARM, ARM64) +- Run test suites on all platforms (using QEMU emulation for ARM on Linux) +- Commit updated libraries back to the repository on successful builds + +See `.github/workflows/buildcommit.yml` for the complete CI configuration. ## Development ### Adding new platforms -1. Modify `platform.h` to identif the platform environment. Define an ABI name and +1. Modify `platform.h` to identify the platform environment. Define an ABI name and include custom header files. 2. Use the `switch_template.h` to help build a `switch_ABI.h` file for your ABI. 3. Provide an assembler version, `switch_ABI.S` by compiling the `gen_asm.c` file for your platform. diff --git a/lib/aarch64/libstackman.a b/lib/aarch64/libstackman.a index c30dcd0..b6301ce 100644 Binary files a/lib/aarch64/libstackman.a and b/lib/aarch64/libstackman.a differ diff --git a/lib/arm32/libstackman.a b/lib/arm32/libstackman.a index 426030f..64a81d9 100644 Binary files a/lib/arm32/libstackman.a and b/lib/arm32/libstackman.a differ diff --git a/lib/sysv_amd64/libstackman.a b/lib/sysv_amd64/libstackman.a index 8b38241..367dabb 100644 Binary files a/lib/sysv_amd64/libstackman.a and b/lib/sysv_amd64/libstackman.a differ diff --git a/lib/sysv_i386/libstackman.a b/lib/sysv_i386/libstackman.a index 1d91d87..0dbeb87 100644 Binary files a/lib/sysv_i386/libstackman.a and b/lib/sysv_i386/libstackman.a differ diff --git a/lib/win_arm/stackman.lib b/lib/win_arm/stackman.lib index 0b34d11..b45daad 100644 Binary files a/lib/win_arm/stackman.lib and b/lib/win_arm/stackman.lib differ diff --git a/lib/win_arm64/stackman.lib b/lib/win_arm64/stackman.lib index eb3917c..95ab7be 100644 Binary files a/lib/win_arm64/stackman.lib and b/lib/win_arm64/stackman.lib differ diff --git a/lib/win_x64/stackman.lib b/lib/win_x64/stackman.lib index f51e16e..cc48de4 100644 Binary files a/lib/win_x64/stackman.lib and b/lib/win_x64/stackman.lib differ diff --git a/lib/win_x86/stackman.lib b/lib/win_x86/stackman.lib index 90c292e..6409579 100644 Binary files a/lib/win_x86/stackman.lib and b/lib/win_x86/stackman.lib differ diff --git a/stackman/platforms/switch_template.h b/stackman/platforms/switch_template.h index 149209f..850e051 100644 --- a/stackman/platforms/switch_template.h +++ b/stackman/platforms/switch_template.h @@ -31,7 +31,7 @@ void *STACKMAN_SWITCH_INASM_NAME(stackman_cb_t callback, void *context) /* assembly to save non-volatile registers * those, according to abi, that functions must save/restore if they * intend to use them - /* __asm__("push volatile registers") */ + /* __asm__("push non-volatile registers") */ /* sp = get stack pointer from assembly code */ /* __asm__("get sp into stack_pointer") */ @@ -41,7 +41,7 @@ void *STACKMAN_SWITCH_INASM_NAME(stackman_cb_t callback, void *context) /* __asm__("store sp from stack_pointer") */ stack_pointer = callback(context, STACKMAN_OP_RESTORE, stack_pointer); /* restore non-volatile registers from stack */ - /* __asm__("pop volatile registers") */ + /* __asm__("pop non-volatile registers") */ return stack_pointer; } diff --git a/stackman/platforms/switch_x64_msvc.asm b/stackman/platforms/switch_x64_msvc.asm index 998eb24..1ff34dc 100644 --- a/stackman/platforms/switch_x64_msvc.asm +++ b/stackman/platforms/switch_x64_msvc.asm @@ -49,7 +49,7 @@ NESTED_ENTRY stackman_switch, _TEXT$00 .allocstack 20h .endprolog - ;save argments in nonvolatile registers + ;save arguments in non-volatile registers mov r12, rcx ;callback mov r13, rdx ;context @@ -111,7 +111,7 @@ stackman_call PROC FRAME .setframe rbp, 00h .endprolog - ; suffle arguments into volatile registers + ; shuffle arguments into volatile registers mov rax, rcx ; callback mov rcx, rdx ; context into first arg mov r9, r8 ; and stack pointer in volatile registers diff --git a/stackman/stackman.h b/stackman/stackman.h index 4a2bf78..440b2e6 100644 --- a/stackman/stackman.h +++ b/stackman/stackman.h @@ -2,6 +2,19 @@ #ifndef STACKMAN_H #define STACKMAN_H +/* Version information */ +#define STACKMAN_VERSION_MAJOR 1 +#define STACKMAN_VERSION_MINOR 0 +#define STACKMAN_VERSION_PATCH 0 + +/* Version as a string */ +#define STACKMAN_VERSION "1.0.0" + +/* Version as a single number for comparisons (MMmmpp: Major, minor, patch) */ +#define STACKMAN_VERSION_NUMBER ((STACKMAN_VERSION_MAJOR * 10000) + \ + (STACKMAN_VERSION_MINOR * 100) + \ + STACKMAN_VERSION_PATCH) + /* the main include file. The following macros can be defined before including * STACKMAN_OPTIONAL - Do not error if the platform isn't supported * STACKMAN_VERBOSE - Emit the found platform to output diff --git a/stackman/stackman_impl.h b/stackman/stackman_impl.h index 36bd372..378145e 100644 --- a/stackman/stackman_impl.h +++ b/stackman/stackman_impl.h @@ -8,7 +8,7 @@ * defines: * STACKMAN_LINKAGE_STATIC - define to provide static linkage to stackman_switch() * - * See also stackman.h for main incle api + * See also stackman.h for main include api */ #define STACKMAN_SWITCH_IMPL diff --git a/stackman/stackman_switch.h b/stackman/stackman_switch.h index 3694cdf..1ed63e0 100644 --- a/stackman/stackman_switch.h +++ b/stackman/stackman_switch.h @@ -55,7 +55,7 @@ * stack pointer. * * The implementation must simply: - * 1) store all platform and cpu state on the stack (callee-stored + * 1) store all platform and cpu state on the stack (callee-saved * registers, exception state, etc) * 2) call the callback with the context, opcode STACKMAN_OP_SAVE and * the current stack pointer. This allows the application to do additional @@ -92,7 +92,7 @@ typedef enum stackman_op_t { STACKMAN_OP_SAVE = 0, /* The callback receives the new stack pointer and should restore - * any state for it, e.g. fillint the stack with the correct data. + * any state for it, e.g. filling the stack with the correct data. * what it returns will be the return value from stackman_switch(). */ STACKMAN_OP_RESTORE = 1, diff --git a/tests/test.c b/tests/test.c index a8beffc..30bc01a 100644 --- a/tests/test.c +++ b/tests/test.c @@ -9,9 +9,9 @@ #define assert_align(p) assert((intptr_t)(p) == STACKMAN_SP_ALIGN(p)) -/* simple test ithout modifying stack pointers. - * test that the callback is called wiht valid - * stackpoiners and the stage member in the +/* simple test without modifying stack pointers. + * test that the callback is called with valid + * stack pointers and the stage member in the * correct order. */ typedef struct ctxt01 diff --git a/tests/test_cc.cc b/tests/test_cc.cc index 3e6a6da..99546b4 100644 --- a/tests/test_cc.cc +++ b/tests/test_cc.cc @@ -6,9 +6,9 @@ #include using namespace std; -/* simple test ithout modifying stack pointers. - * test that the callback is called wiht valid - * stackpoiners and the stage member in the +/* simple test without modifying stack pointers. + * test that the callback is called with valid + * stack pointers and the stage member in the * correct order. */ typedef struct ctxt01