Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .github/workflows/buildcommit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@ jobs:
strategy:
fail-fast: false
matrix:
name: [AMD64, i386, arm, aarch64, riscv64]
name: [AMD64, i386, arm, armhf, aarch64, riscv64]
include:
- name: i386
platformflags: -m32
- name: arm
platformtools: arm-linux-gnueabi
emulator: qemu-arm
- name: armhf
platformtools: arm-linux-gnueabihf
emulator: qemu-arm
- name: aarch64
platformtools: aarch64-linux-gnu
emulator: qemu-aarch64
Expand Down Expand Up @@ -205,6 +208,7 @@ jobs:
cp -r artifacts/sysv_amd64 release/lib/
cp -r artifacts/sysv_i386 release/lib/
cp -r artifacts/arm32 release/lib/
cp -r artifacts/armhf release/lib/
cp -r artifacts/aarch64 release/lib/
cp -r artifacts/riscv64 release/lib/
cp -r artifacts/darwin_x86_64 release/lib/
Expand Down Expand Up @@ -246,7 +250,8 @@ jobs:
lib/
sysv_amd64/ - Linux x86_64
sysv_i386/ - Linux x86 (32-bit)
arm32/ - Linux ARM (32-bit, AAPCS)
arm32/ - Linux ARM (32-bit, AAPCS soft-float)
armhf/ - Linux ARM (32-bit, AAPCS-VFP hard-float)
aarch64/ - Linux ARM64 (AAPCS64)
riscv64/ - Linux RISC-V 64-bit (LP64D)
darwin_x86_64/ - macOS x86_64 (Intel)
Expand Down
24 changes: 14 additions & 10 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ and the stack direction.

## Platforms

Linux: `sysv_i386`, `sysv_amd64`, `arm32` (soft-float AAPCS), `aarch64`,
`riscv64` (LP64D). macOS: `darwin_x86_64`, `darwin_arm64`. Windows:
Linux: `sysv_i386`, `sysv_amd64`, `arm32` (soft-float AAPCS), `armhf`
(AAPCS-VFP), `aarch64`, `riscv64` (LP64D). macOS: `darwin_x86_64`,
`darwin_arm64`. Windows:
`win_x86`, `win_x64`, `win_arm64`. `win_aarch64` is accepted and
normalized to `win_arm64`. FreeBSD, OpenBSD, and NetBSD use the Linux
`sysv_amd64` sources. Their workflow runs `gmake`.
Expand All @@ -55,11 +56,12 @@ preserved.
Details and the original audit are in `docs/abi-register-audit.md`.
Limits that are easy to fix the wrong way:

- ARM32 hard-float is unsupported. `switch_arm_gcc.S` is `.arch armv5t`
and `.fpu softvfp`, and it does not spill `d8`–`d15`. `__ARM_PCS_VFP`
is a compile error. A hard-float port is a new assembly file with its
own ABI name. Softfp does not define `__ARM_PCS_VFP`, and the compiler
can still use `d8`–`d15`.
- `arm32` is the soft-float assembly (`switch_arm_gcc.S`, `.arch armv5t`,
`.fpu softvfp`). It does not spill `d8`–`d15`, and `__ARM_PCS_VFP` is
a compile error in that header. `armhf` (`switch_armhf_gcc.S`) is the
hard-float port and is what `__ARM_PCS_VFP` selects. It spills
`d8`–`d15` in `stackman_switch`. It does not spill `FPSCR`. Softfp
does not define `__ARM_PCS_VFP` and still builds as `arm32`.
- `aarch64`, `darwin_arm64`, and `win_arm64` do not preserve SME ZA
across `stackman_switch`. Do not call `__arm_za_disable`. That helper
commits the outgoing caller's ZA and turns ZA off. Nothing in the
Expand All @@ -85,7 +87,8 @@ Limits that are easy to fix the wrong way:
make test # native ABI. Builds lib/$(ABI)/libstackman.a and runs the tests.
make abiname
make PLATFORMFLAGS=-m32 test
make PLATFORM_PREFIX=arm-linux-gnueabi- EMULATOR=qemu-arm test
make PLATFORM_PREFIX=arm-linux-gnueabi EMULATOR=qemu-arm test
make PLATFORM_PREFIX=arm-linux-gnueabihf EMULATOR=qemu-arm test
```

`make clean` only when switching `PLATFORMFLAGS` or `PLATFORM_PREFIX`.
Expand Down Expand Up @@ -118,8 +121,9 @@ major version. `commit-artifacts` in

`.github/workflows/buildcommit.yml`, on pull requests to `master`:

- `build-linux-gnu`: AMD64, i386 (`-m32`), arm (`qemu-arm`), aarch64
(`qemu-aarch64`), riscv64 (`qemu-riscv64`). Each runs `make test`.
- `build-linux-gnu`: AMD64, i386 (`-m32`), arm (`qemu-arm`), armhf
(`qemu-arm`), aarch64 (`qemu-aarch64`), riscv64 (`qemu-riscv64`).
Each runs `make test`.
- `build-macos`: `macos-15-intel` (`darwin_x86_64`) and `macos-latest`
(`darwin_arm64`).
- `build-windows`: VS2022 `x86`, `x64`, and `arm64`. Tests run only
Expand Down
32 changes: 21 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
║ ║
║ Do you have a stack? Stackman is here to help. ║
║ ║
║ v1.2.5 • 10 platforms • Stack switching simplified ║
║ v1.2.5 • 11 platforms • Stack switching simplified ║

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] The banner now says 11 platforms and the ABI list adds armhf, but two later sections of the same README were not updated. Line 192 still says pre-built libraries cover "9 ABIs total: 4 Linux, 2 macOS, 3 Windows" (actual set is 6 Linux + 2 macOS + 3 Windows). Line 222 still says CI builds "all 9 supported platforms" and lists Linux as AMD64, i386, ARM32, and ARM64, omitting both armhf and riscv64.

Suggestion: Update those two sentences to the same 11-ABI set the banner, the platform list, and create-release now use, including armhf in the Linux CI list.

║ ║
╚══════════════════════════════════════════════════════════════╝
```
Expand Down Expand Up @@ -92,7 +92,8 @@ calling convention, plus archive format:
- **Linux (System V ABI)**
- sysv_i386 (32-bit x86)
- sysv_amd64 (64-bit x86_64)
- arm32 (32-bit ARM, AAPCS)
- arm32 (32-bit ARM, AAPCS soft-float)
- armhf (32-bit ARM, AAPCS-VFP hard-float)
- aarch64 (64-bit ARM, AAPCS64)
- riscv64 (64-bit RISC-V, LP64D ABI)
- **macOS (Darwin)**
Expand Down Expand Up @@ -188,7 +189,7 @@ There are two basic ways to add the library to your project: Using a static libr

- Download pre-built libraries from the [Releases page](https://github.com/kristjanvalur/stackman/releases) for your platform
- Alternatively, link with the `libstackman.a` or `stackman.lib` libraries in the `lib/` directory if you've cloned the repository
- Pre-built libraries are available for all supported platforms (9 ABIs total: 4 Linux, 2 macOS, 3 Windows)
- Pre-built libraries are available for all supported platforms (11 ABIs total: 6 Linux, 2 macOS, 3 Windows)
- Libraries are automatically rebuilt by CI and committed to the repository for easy integration

### inlined code
Expand All @@ -206,7 +207,7 @@ assembly files is generally preferred for reliability and compatibility.
**Platforms with inline assembly support:**
- x86 (sysv_i386)
- x86_64 (sysv_amd64)
- ARM32 (arm32)
- ARM32 soft-float (arm32) and hard-float (armhf)
- ARM64 (aarch64)

Only when compiled with GCC and `STACKMAN_INLINE_ASM` is not disabled. Clang, RISC-V, macOS, and Windows
Expand All @@ -218,7 +219,7 @@ over separate assembly language source.
## Continuous Integration

The project uses GitHub Actions to automatically:
- Build libraries for all 9 supported platforms (Linux: AMD64, i386, ARM32, ARM64; macOS: x86_64, ARM64; Windows: x86, x64, ARM64)
- Build libraries for all 11 supported platforms (Linux: AMD64, i386, ARM32, armhf, ARM64, RISC-V; macOS: x86_64, ARM64; Windows: x86, x64, ARM64)
- Run test suites on all platforms (using QEMU emulation for ARM on Linux)
- Commit updated libraries back to the repository on successful builds (for development branches)
- Create GitHub Releases with downloadable libraries when version tags are pushed
Expand Down Expand Up @@ -250,7 +251,8 @@ Linux on x86-64 can be used to cross compile for x86 and ARM targets. This is m
stackman/platform/gen_asm.c

- x86 requires the -m32 flag to compilers and linkers.
- arm32 requires to use the arm-linux-gnueabi-* tools, including cc and linker
- arm32 requires the arm-linux-gnueabi-* tools, including cc and linker
- armhf requires the arm-linux-gnueabihf-* tools, including cc and linker
- aarch64 requires the aarch64-linux-gnu-* tools.

The x86 tools require the **gcc-multilib** and **g++-multilib** packages to be installed. They, however, can't co-exist with the **gcc-arm-linux-gnueabi** or
Expand All @@ -264,14 +266,15 @@ target intent may not be fully visible to that probe. Set `STACKMAN_ABI` to forc
library ABI deterministically.

Supported values match the ABI names in this document (for example: `sysv_i386`, `sysv_amd64`, `arm32`,
`aarch64`, `riscv64`, `darwin_x86_64`, `darwin_arm64`, `win_x86`, `win_x64`, `win_arm64`).
`armhf`, `aarch64`, `riscv64`, `darwin_x86_64`, `darwin_arm64`, `win_x86`, `win_x64`, `win_arm64`).
For compatibility, `win_aarch64` is also accepted and normalized to `win_arm64`.

Examples:

- `make STACKMAN_ABI=sysv_i386 PLATFORMFLAGS=-m32 test`
- `make STACKMAN_ABI=arm32 PLATFORM_PREFIX=arm-linux-gnueabi- EMULATOR=qemu-arm test`
- `make STACKMAN_ABI=win_arm64 PLATFORM_PREFIX=aarch64-w64-mingw32- all`
- `make STACKMAN_ABI=arm32 PLATFORM_PREFIX=arm-linux-gnueabi EMULATOR=qemu-arm test`
- `make STACKMAN_ABI=armhf PLATFORM_PREFIX=arm-linux-gnueabihf EMULATOR=qemu-arm test`
- `make STACKMAN_ABI=win_arm64 PLATFORM_PREFIX=aarch64-w64-mingw32 all`

#### Cross compiling for x86 (32 bit) on Linux

Expand All @@ -284,14 +287,21 @@ Examples:
- install __gcc-arm-linux-gnueabi__ and __g++-arm-linux-gnueabi__
- install __qemu-user__ for hardware emulation
- *compile* **gen_asm.c** using `arm-linux-gnueabi-gcc`
- *make* using `make PLATFORM_PREFIX=arm-linux-gnueabi- EMULATOR=qemu-arm test`
- *make* using `make PLATFORM_PREFIX=arm-linux-gnueabi EMULATOR=qemu-arm test`

#### Cross compiling for ARM hard-float (32 bit) on Linux

- install __gcc-arm-linux-gnueabihf__ and __g++-arm-linux-gnueabihf__
- install __qemu-user__ for hardware emulation
- *compile* **gen_asm.c** using `arm-linux-gnueabihf-gcc -marm`
- *make* using `make PLATFORM_PREFIX=arm-linux-gnueabihf EMULATOR=qemu-arm test`

#### Cross compiling for Arm64 on Linux

- install **gcc-aarch64-linux-gnu** and **g++-aarch64-linux-gnu**
- install __qemu-user__ for hardware emulation
- *compile* using `aarch64-linux-gnu-gcc`
- *make* using `make PLATFORM_PREFIX=aarch64-linux-gnu- EMULATOR=qemu-aarch64 test`
- *make* using `make PLATFORM_PREFIX=aarch64-linux-gnu EMULATOR=qemu-aarch64 test`

## A note about Intel CET

Expand Down
28 changes: 16 additions & 12 deletions docs/abi-register-audit.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ file and does not run it.
| `switch_x64_msvc.asm` | `rbx`, `rbp`, `rdi`, `rsi`, `r12`–`r15`, `xmm6`–`xmm15` | Those registers match the Windows x64 ABI. MXCSR[6:15] is non-volatile on Windows and is not spilled, so a changed rounding mode does not travel with the stack. The SysV file does save MXCSR. |
| `switch_x86_gcc.S` | `ebx`, `esi`, `edi`, `ebp` | Integer set is complete. There is no callee-saved XMM on i386 SysV. The x87 control word is callee-saved in that ABI and is not saved. |
| `switch_x86_msvc.asm` | `ebx`, `esi`, `edi`, `ebp`, plus the C wrapper saves `fs:[0]` | Integer set is complete for cdecl. Same x87 control-word gap. |
| `switch_arm_gcc.S` | `r4`–`r11`, `lr` | Complete for the soft-float `arm-linux-gnueabi` ABI this tree ships (`.arch armv5t`, `.fpu softvfp`). `d8`–`d15` are absent. The inline header clobbers them, so a hard-float or softfp build of this `.S` would drop callee-saved VFP state. `armhf` is not a configured ABI. The live frame pointer is not adjusted. |
| `switch_arm_gcc.S` | `r4`–`r11`, `lr` | Complete for the soft-float `arm-linux-gnueabi` ABI this tree ships (`.arch armv5t`, `.fpu softvfp`). `d8`–`d15` are absent. The inline header clobbers them, so a hard-float or softfp build of this `.S` would drop callee-saved VFP state. The live frame pointer is not adjusted. |
| `switch_arm_msvc.asm` | `r4`–`r11`, `lr`, `q4`–`q7` (`d8`–`d15`), `FPSCR` | The set matches 32-bit Windows ARM. Alignment does not. `push {r4-r10, fp, lr}` is 9 words, so `sp` is 4-byte aligned for `vpush {q4-q7}`. A doubleword `VPUSH` requires 8-byte alignment. `stackman_call` pushes 3 registers and then `blx`s with `sp` still 4 mod 8. The changelog records this ABI as dropped; the file is still wrong if it is built. |
| `switch_riscv64_gcc.S` | `ra`, `s0`–`s11`, `fs0`–`fs11` | `stackman_switch` is complete for LP64D. `gp` and `tp` are left alone. `fcsr` is thread `fenv` state, not a per-call spill. |

Expand Down Expand Up @@ -186,6 +186,9 @@ file and does not run it.
keep the callback or the new stack pointer in `r11` across that asm.
AArch64 still adjusts `x29`, which the prologue sets with
`mov x29, sp`.
- `__ARM_PCS_VFP` selects `armhf` (`switch_armhf_gcc.S`), which spills
`d8`–`d15`. `FPSCR` is still not spilled. The soft-float file was
left unchanged.
- Windows ARM32 was removed: `switch_arm_msvc.asm`, `switch_arm_msvc.h`,
the `_M_ARM` branch in `platform.h`, the Debug|ARM configurations in
vs2017, vs2019, and vs2022, and `lib/win_arm/stackman.lib`. The
Expand Down Expand Up @@ -356,7 +359,8 @@ The shipped integer set (`r4`–`r11`, `lr`) needs no new stores.
**Applied fix.** `__ARM_PCS_VFP` is a compile error, and `CP_REGISTERS` is
commented as inline-only. The optional `r11` adjustment was applied and
then removed. See the frame-pointer applied fix above. `d8`–`d15` were
not added. A separate hard-float port is on the roadmap.
not added to this file. `__ARM_PCS_VFP` selects `armhf`
(`switch_armhf_gcc.S` spills `d8`–`d15`; `FPSCR` is still not spilled).

### Windows ARM32: delete it, or align `sp` before `vpush`

Expand Down Expand Up @@ -393,20 +397,20 @@ alternative was not used.

## Roadmap

Two items from the audit are still open.
SME lazy save is still open. The ARM32 hard-float port is `armhf`.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] The roadmap now says the ARM32 hard-float port is armhf, but earlier sections of this file still describe that port as future work. The findings table (line 161) says "armhf is not a configured ABI." The soft-float applied fix (line 359) says "A separate hard-float port is on the roadmap." Those sentences now contradict the roadmap and platform.h.

Suggestion: Leave the original soft-float findings in place, and add a short applied-fix note that __ARM_PCS_VFP now selects armhf (switch_armhf_gcc.S spills d8–d15; FPSCR is still not spilled). Drop or qualify "on the roadmap" / "not a configured ABI."


### ARM32 hard-float

The shipped `switch_arm_gcc.S` is soft-float (`arm-linux-gnueabi`,
The soft-float file `switch_arm_gcc.S` is unchanged (`arm-linux-gnueabi`,
`.arch armv5t`, `.fpu softvfp`) and does not spill `d8`–`d15`.
`__ARM_PCS_VFP` is a compile error, so a hard-float build cannot use that
file by mistake.

A hard-float port is a new assembly, alongside the soft-float file. It
spills `d8`–`d15` in `stackman_switch`, has its own ABI name, and is what
a `__ARM_PCS_VFP` build selects. The `#error` stays until that port
exists. Softfp is not that port: it does not define `__ARM_PCS_VFP`, and
the compiler can still use `d8`–`d15`.
`__ARM_PCS_VFP` is still a compile error in that header, so a hard-float
build cannot use that file by mistake.

`switch_armhf_gcc.h` is the hard-float port. `platform.h` selects it,
and the ABI name `armhf`, when `__ARM_PCS_VFP` is set. `stackman_switch`
spills `d8`–`d15`. `FPSCR` is not spilled. Softfp is not this port: it
does not define `__ARM_PCS_VFP`, and the compiler can still use
`d8`–`d15` while building `arm32`.

### AArch64 SME lazy save

Expand Down
16 changes: 14 additions & 2 deletions stackman/platforms/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,15 @@
#define _STACKMAN_PLATFORM x86_clang
#define _STACKMAN_ABI sysv_i386
#elif defined(__arm__)
#include "switch_arm_gcc.h" /* gcc using arm */
#if defined(__ARM_PCS_VFP)
#include "switch_armhf_gcc.h" /* hard-float AAPCS-VFP */
#define _STACKMAN_PLATFORM armhf_clang
#define _STACKMAN_ABI armhf
#else
#include "switch_arm_gcc.h" /* soft-float AAPCS */
#define _STACKMAN_PLATFORM arm32_clang
#define _STACKMAN_ABI arm32
#endif
#elif defined(__ARM_ARCH_ISA_A64)
#include "switch_aarch64_gcc.h" /* gcc using arm aarch64*/
#define _STACKMAN_PLATFORM aarch64_clang
Expand Down Expand Up @@ -92,9 +98,15 @@
#define _STACKMAN_PLATFORM x86_gcc
#define _STACKMAN_ABI sysv_i386
#elif defined(__arm__)
#include "switch_arm_gcc.h" /* gcc using arm */
#if defined(__ARM_PCS_VFP)
#include "switch_armhf_gcc.h" /* hard-float AAPCS-VFP */
#define _STACKMAN_PLATFORM armhf_gcc
#define _STACKMAN_ABI armhf
#else
#include "switch_arm_gcc.h" /* soft-float AAPCS */
#define _STACKMAN_PLATFORM arm32_gcc
#define _STACKMAN_ABI arm32
#endif
#elif defined(__ARM_ARCH_ISA_A64)
#include "switch_aarch64_gcc.h" /* gcc using arm aarch64*/
#define _STACKMAN_PLATFORM aarch64_gcc
Expand Down
101 changes: 101 additions & 0 deletions stackman/platforms/switch_armhf_gcc.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* this file is provided for where inline assembly is not
* dependable, such as with clang, or if
* STACKMAN_INLINE_ASM is false
* The file is generated using
* "arm-linux-gnueabihf-gcc -S -marm gen_asm.c" and then copying the code
* from gen_asm.s into this file.
*
*/
.arch armv7-a
.fpu vfpv3-d16
.eabi_attribute 28, 1
.eabi_attribute 20, 1
.eabi_attribute 21, 1
.eabi_attribute 23, 3
.eabi_attribute 24, 1
.eabi_attribute 25, 1
.eabi_attribute 26, 2
.eabi_attribute 30, 6
.eabi_attribute 34, 1
.eabi_attribute 18, 4
.file "gen_asm.c"
.text
.align 2
.global stackman_switch
.syntax unified
.arm
.type stackman_switch, %function
stackman_switch:
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 0, uses_anonymous_args = 0
push {r3, r4, r5, r6, r7, r8, r9, r10, fp, lr}
vpush.64 {d8, d9, d10, d11, d12, d13, d14, d15}
mov r3, r0
mov r0, r1
.syntax divided
@ 91 "../platforms/switch_armhf_gcc.h" 1
mov r2, sp
@ 0 "" 2
.arm
.syntax unified
mov r1, #0
mov r5, r0
mov r4, r3
blx r3
mov r2, r0
.syntax divided
@ 95 "../platforms/switch_armhf_gcc.h" 1
mov sp, r0
@ 0 "" 2
.arm
.syntax unified
mov r1, #1
mov r0, r5
blx r4
vldm sp!, {d8-d15}
pop {r3, r4, r5, r6, r7, r8, r9, r10, fp, pc}
.size stackman_switch, .-stackman_switch
.align 2
.global stackman_call
.syntax unified
.arm
.type stackman_call, %function
stackman_call:
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 1, uses_anonymous_args = 0
push {r4, r5, fp, lr}
add fp, sp, #12
mov r5, r0
mov r0, r1
mov r3, r2
.syntax divided
@ 111 "../platforms/switch_armhf_gcc.h" 1
mov r4, sp
@ 0 "" 2
@ 112 "../platforms/switch_armhf_gcc.h" 1
mov r2, sp
@ 0 "" 2
.arm
.syntax unified
cmp r3, #0
beq .L4
.syntax divided
@ 116 "../platforms/switch_armhf_gcc.h" 1
mov sp, r3
@ 0 "" 2
.arm
.syntax unified
.L4:
mov r1, #2
blx r5
.syntax divided
@ 120 "../platforms/switch_armhf_gcc.h" 1
mov sp, r4
@ 0 "" 2
.arm
.syntax unified
pop {r4, r5, fp, pc}
.size stackman_call, .-stackman_call
.ident "GCC: (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0"
.section .note.GNU-stack,"",%progbits
Loading
Loading