diff --git a/.github/workflows/buildcommit.yml b/.github/workflows/buildcommit.yml index 6dc8fd8..31f63ae 100644 --- a/.github/workflows/buildcommit.yml +++ b/.github/workflows/buildcommit.yml @@ -16,7 +16,7 @@ jobs: strategy: fail-fast: false matrix: - name: [AMD64, i386, arm, aarch64] + name: [AMD64, i386, arm, aarch64, riscv64] include: - name: i386 platformflags: -m32 @@ -26,6 +26,9 @@ jobs: - name: aarch64 platformtools: aarch64-linux-gnu emulator: qemu-aarch64 + - name: riscv64 + platformtools: riscv64-linux-gnu + emulator: qemu-riscv64 # name: build-linux-gnu (${{matrix.name}}) env: PLATFORMFLAGS: ${{matrix.platformflags}} @@ -156,6 +159,7 @@ jobs: cp -r artifacts/sysv_i386 release/lib/ cp -r artifacts/arm32 release/lib/ cp -r artifacts/aarch64 release/lib/ + cp -r artifacts/riscv64 release/lib/ cp -r artifacts/darwin_x86_64 release/lib/ cp -r artifacts/darwin_arm64 release/lib/ cp -r artifacts/win_x86 release/lib/ @@ -197,6 +201,7 @@ jobs: sysv_i386/ - Linux x86 (32-bit) arm32/ - Linux ARM (32-bit, AAPCS) aarch64/ - Linux ARM64 (AAPCS64) + riscv64/ - Linux RISC-V 64-bit (LP64D) darwin_x86_64/ - macOS x86_64 (Intel) darwin_arm64/ - macOS ARM64 (Apple Silicon) win_x86/ - Windows x86 (32-bit) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cb1f4a..495227c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,22 @@ 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.2.0] - 2025-11-16 + +### Added +- RISC-V platform support + - `riscv64` - RISC-V 64-bit (LP64D ABI with hardware double-precision floating point) +- Platform detection for RISC-V in `platforms/platform.h` using `__riscv` and `__riscv_xlen` macros +- Assembly implementation in `platforms/switch_riscv64_gcc.S` + - Full register preservation (s0-s11, fs0-fs11, ra) + - 16-byte stack alignment per RISC-V ABI + - Both `stackman_switch` and `stackman_call` functions +- RISC-V build job in CI workflow with qemu-riscv64 emulation +- RISC-V library included in release archives + +### Changed +- Release archives now contain 10 platform libraries (was 9) + ## [1.1.0] - 2025-11-16 ### Added diff --git a/README.md b/README.md index eab378a..abee91c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # stackman -**Version 1.1.0** +**Version 1.2.0** Simple low-level stack manipulation API and implementation for common platforms @@ -80,6 +80,7 @@ calling convention, plus archive format: - sysv_amd64 (64-bit x86_64) - arm32 (32-bit ARM, AAPCS) - aarch64 (64-bit ARM, AAPCS64) + - riscv64 (64-bit RISC-V, LP64D ABI) - **macOS (Darwin)** - darwin_x86_64 (Intel) - darwin_arm64 (Apple Silicon) diff --git a/stackman/platforms/platform.h b/stackman/platforms/platform.h index 007b852..daf3102 100644 --- a/stackman/platforms/platform.h +++ b/stackman/platforms/platform.h @@ -74,6 +74,10 @@ #else #define _STACKMAN_ABI aarch64 #endif +#elif defined(__riscv) && (__riscv_xlen == 64) +#include "switch_riscv64_gcc.h" /* gcc using riscv64 */ +#define _STACKMAN_PLATFORM riscv64_clang +#define _STACKMAN_ABI riscv64 #endif #endif /* __clang__ */ @@ -103,6 +107,10 @@ #else #define _STACKMAN_ABI aarch64 #endif +#elif defined(__riscv) && (__riscv_xlen == 64) +#include "switch_riscv64_gcc.h" /* gcc using riscv64 */ +#define _STACKMAN_PLATFORM riscv64_gcc +#define _STACKMAN_ABI riscv64 #endif #endif /* __GNUC__ */ diff --git a/stackman/platforms/switch_riscv64_gcc.S b/stackman/platforms/switch_riscv64_gcc.S new file mode 100644 index 0000000..54d97c1 --- /dev/null +++ b/stackman/platforms/switch_riscv64_gcc.S @@ -0,0 +1,227 @@ +/* + * RISC-V RV64 (LP64D ABI) stack switching implementation + * + * This file provides stack manipulation for RISC-V 64-bit (RV64) using the + * LP64D ABI (64-bit pointers, hardware double-precision floating point). + * + * RISC-V RV64 LP64D Calling Convention: + * - Arguments: a0-a7 (x10-x17) + * - Return: a0-a1 (x10-x11) + * - Callee-saved: s0-s11 (x8-x9, x18-x27), sp (x2) + * - Caller-saved: a0-a7, t0-t6, ra (x1) + * - Stack pointer: sp (x2) - must be 16-byte aligned + * - Frame pointer: s0/fp (x8) - optional + */ + +/* ELF metadata and CFI directives */ +#define FUNCTION(name) .globl name ; .type name, @function +#define LABEL(name) name: +#define TYPE_FUNCTION(name) .type name, @function +#define SIZE_FUNCTION(name) .size name, .-name +#define GNU_STACK .section .note.GNU-stack,"",@progbits +#define CFI_STARTPROC .cfi_startproc +#define CFI_ENDPROC .cfi_endproc +#define CFI_DEF_CFA_OFFSET(x) .cfi_def_cfa_offset x +#define CFI_OFFSET(r,o) .cfi_offset r, o +#define CFI_RESTORE(r) .cfi_restore r + + .file "switch_riscv64_gcc.S" + .text + .align 1 + +/* + * void *stackman_switch(stackman_cb_t callback, void *context) + * + * Saves current stack state, calls callback twice with stack pointer, + * and switches to new stack returned by first callback invocation. + * + * Register usage: + * - a0: callback function pointer (first arg) + * - a1: context pointer (second arg) + * - Callee-saved regs: s0-s11 must be preserved + * - FP regs: fs0-fs11 (callee-saved floating point) + */ + FUNCTION(stackman_switch) +LABEL(stackman_switch) +.LFB0: + CFI_STARTPROC + + /* Allocate stack frame (160 bytes) + * Layout: ra(8) + s0-s11(12*8=96) + fs0-fs11(12*8=96) = 200 bytes + * Round to 208 for 16-byte alignment + */ + addi sp, sp, -208 + CFI_DEF_CFA_OFFSET(208) + + /* Save return address and callee-saved registers */ + sd ra, 200(sp) + CFI_OFFSET(1, -8) /* ra is x1 */ + sd s0, 192(sp) + CFI_OFFSET(8, -16) + sd s1, 184(sp) + CFI_OFFSET(9, -24) + sd s2, 176(sp) + CFI_OFFSET(18, -32) + sd s3, 168(sp) + CFI_OFFSET(19, -40) + sd s4, 160(sp) + CFI_OFFSET(20, -48) + sd s5, 152(sp) + CFI_OFFSET(21, -56) + sd s6, 144(sp) + CFI_OFFSET(22, -64) + sd s7, 136(sp) + CFI_OFFSET(23, -72) + sd s8, 128(sp) + CFI_OFFSET(24, -80) + sd s9, 120(sp) + CFI_OFFSET(25, -88) + sd s10, 112(sp) + CFI_OFFSET(26, -96) + sd s11, 104(sp) + CFI_OFFSET(27, -104) + + /* Save callee-saved floating point registers */ + fsd fs0, 96(sp) + fsd fs1, 88(sp) + fsd fs2, 80(sp) + fsd fs3, 72(sp) + fsd fs4, 64(sp) + fsd fs5, 56(sp) + fsd fs6, 48(sp) + fsd fs7, 40(sp) + fsd fs8, 32(sp) + fsd fs9, 24(sp) + fsd fs10, 16(sp) + fsd fs11, 8(sp) + + /* Preserve callback and context in callee-saved registers */ + mv s2, a0 /* s2 = callback */ + mv s3, a1 /* s3 = context */ + + /* First callback: callback(context, opcode, sp) */ + mv a0, s3 /* a0 = context */ + li a1, 0 /* a1 = STACKMAN_OP_SAVE */ + mv a2, sp /* a2 = current sp */ + jalr s2 /* call callback */ + + /* Save returned pointer and switch stack */ + mv s4, a0 /* s4 = returned value */ + mv sp, a0 /* switch to new stack */ + + /* Second callback: callback(context, opcode, sp) */ + mv a0, s3 /* a0 = context */ + li a1, 1 /* a1 = STACKMAN_OP_RESTORE */ + mv a2, sp /* a2 = new sp */ + jalr s2 /* call callback */ + + /* Restore callee-saved floating point registers */ + fld fs11, 8(sp) + fld fs10, 16(sp) + fld fs9, 24(sp) + fld fs8, 32(sp) + fld fs7, 40(sp) + fld fs6, 48(sp) + fld fs5, 56(sp) + fld fs4, 64(sp) + fld fs3, 72(sp) + fld fs2, 80(sp) + fld fs1, 88(sp) + fld fs0, 96(sp) + + /* Restore callee-saved integer registers */ + ld s11, 104(sp) + CFI_RESTORE(27) + ld s10, 112(sp) + CFI_RESTORE(26) + ld s9, 120(sp) + CFI_RESTORE(25) + ld s8, 128(sp) + CFI_RESTORE(24) + ld s7, 136(sp) + CFI_RESTORE(23) + ld s6, 144(sp) + CFI_RESTORE(22) + ld s5, 152(sp) + CFI_RESTORE(21) + ld s4, 160(sp) + CFI_RESTORE(20) + ld s3, 168(sp) + CFI_RESTORE(19) + ld s2, 176(sp) + CFI_RESTORE(18) + ld s1, 184(sp) + CFI_RESTORE(9) + ld s0, 192(sp) + CFI_RESTORE(8) + ld ra, 200(sp) + CFI_RESTORE(1) + + /* Deallocate frame and return */ + addi sp, sp, 208 + CFI_DEF_CFA_OFFSET(0) + ret + + CFI_ENDPROC +.LFE0: + SIZE_FUNCTION(stackman_switch) + +/* + * void *stackman_call(stackman_cb_t callback, void *context, void *new_stack) + * + * Similar to stackman_switch but uses explicitly provided stack pointer. + * + * Register usage: + * - a0: callback function pointer + * - a1: context pointer + * - a2: new stack pointer + */ + .align 1 + FUNCTION(stackman_call) +LABEL(stackman_call) +.LFB1: + CFI_STARTPROC + + /* Allocate frame for ra and s0 (callee-saved) */ + addi sp, sp, -16 + CFI_DEF_CFA_OFFSET(16) + + /* Save return address and s0 (we'll use it to preserve old sp) */ + sd ra, 8(sp) + CFI_OFFSET(1, -8) + sd s0, 0(sp) + CFI_OFFSET(8, -16) + + /* Save old stack pointer in s0 (callee-saved register) */ + mv s0, sp + + /* Setup args for callback and switch to new stack */ + mv t0, a0 /* t0 = callback */ + mv t1, a2 /* t1 = new_stack (for arg) */ + mv sp, a2 /* switch to new stack */ + + /* Call callback(context, opcode, sp) */ + mv a0, a1 /* a0 = context */ + li a1, 2 /* a1 = STACKMAN_OP_CALL */ + mv a2, t1 /* a2 = new_stack */ + jalr t0 /* call callback */ + + /* Restore old stack pointer from s0 */ + mv sp, s0 + + /* Restore s0 and return address */ + ld s0, 0(sp) + CFI_RESTORE(8) + ld ra, 8(sp) + CFI_RESTORE(1) + + addi sp, sp, 16 + CFI_DEF_CFA_OFFSET(0) + ret + + CFI_ENDPROC +.LFE1: + SIZE_FUNCTION(stackman_call) + +/* Mark stack as non-executable */ +GNU_STACK diff --git a/stackman/platforms/switch_riscv64_gcc.h b/stackman/platforms/switch_riscv64_gcc.h new file mode 100644 index 0000000..8e08597 --- /dev/null +++ b/stackman/platforms/switch_riscv64_gcc.h @@ -0,0 +1,29 @@ +/* + * RISC-V RV64 (LP64D ABI) stack switching - GCC implementation + * + * This implementation uses separate assembly files (.S) for RISC-V 64-bit. + * The LP64D ABI is used: 64-bit pointers with hardware floating point. + */ + +/* Always use assembly source file for RISC-V */ +#if !defined(STACKMAN_ASSEMBLY_SRC) +#define STACKMAN_ASSEMBLY_SRC "platforms/switch_riscv64_gcc.S" +#endif + +#ifndef STACKMAN_SWITCH_IMPL +/* Just check if platform is supported */ +#define STACKMAN_SWITCH_INCLUDED +#else + +#ifndef STACKMAN_HAVE_CALL +#define STACKMAN_HAVE_CALL 1 +#undef STACKMAN_STACK_ALIGN +#define STACKMAN_STACK_ALIGN 16 +#endif + +#if __ASSEMBLER__ && defined(STACKMAN_ASSEMBLY_SRC) +/* Include pre-generated assembly code */ +#include STACKMAN_ASSEMBLY_SRC +#endif + +#endif /* STACKMAN_SWITCH_IMPL */ diff --git a/stackman/stackman.h b/stackman/stackman.h index 483c1e9..257ec54 100644 --- a/stackman/stackman.h +++ b/stackman/stackman.h @@ -4,11 +4,11 @@ /* Version information */ #define STACKMAN_VERSION_MAJOR 1 -#define STACKMAN_VERSION_MINOR 1 +#define STACKMAN_VERSION_MINOR 2 #define STACKMAN_VERSION_PATCH 0 /* Version as a string */ -#define STACKMAN_VERSION "1.1.0" +#define STACKMAN_VERSION "1.2.0" /* Version as a single number for comparisons (MMmmpp: Major, minor, patch) */ #define STACKMAN_VERSION_NUMBER ((STACKMAN_VERSION_MAJOR * 10000) + \