diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index c07847ab..5e7690d2 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -230,6 +230,17 @@ alias asm_sources darwin ; +alias asm_sources + : asm/make_arm64_aapcs_macho_gas.S + asm/jump_arm64_aapcs_macho_gas.S + asm/ontop_arm64_aapcs_macho_gas.S + : aapcs + 64 + arm + mach-o + gcc + ; + # ARM64/AAPCS/PE alias asm_sources : asm/make_arm64_aapcs_pe_armasm.asm @@ -508,6 +519,30 @@ alias asm_sources gcc ; +# SPARC64 +# SPARC64/SYSV/ELF +alias asm_sources + : asm/make_sparc64_sysv_elf_gas.S + asm/jump_sparc64_sysv_elf_gas.S + asm/ontop_sparc64_sysv_elf_gas.S + : sysv + 64 + sparc + elf + clang + ; + +alias asm_sources + : asm/make_sparc64_sysv_elf_gas.S + asm/jump_sparc64_sysv_elf_gas.S + asm/ontop_sparc64_sysv_elf_gas.S + : sysv + 64 + sparc + elf + gcc + ; + # S390X # S390X/SYSV/ELF alias asm_sources diff --git a/doc/architectures.qbk b/doc/architectures.qbk index 8cd33eae..b17e1f22 100644 --- a/doc/architectures.qbk +++ b/doc/architectures.qbk @@ -21,7 +21,7 @@ architectures: [[ppc64] [SYSV|ELF|XCOFF] [-] [SYSV|MACH-O] [-]] [[riscv64] [SYSV|ELF] [-] [SYSV] [-]] [[s390x] [SYSV|ELF] [-] [-] [-]] - [[sparc] [-] [-] [-] [-]] + [[sparc64] [SYSV|ELF] [-] [-] [-]] [[x86_64] [SYSV,X32|ELF] [MS|PE] [SYSV|MACH-O] [-]] ] diff --git a/doc/stack.qbk b/doc/stack.qbk index 4445a807..171a7969 100644 --- a/doc/stack.qbk +++ b/doc/stack.qbk @@ -291,7 +291,7 @@ a stack.]] [variablelist [[Returns:] [Returns a default stack size, which may be platform specific. If the stack is unbounded then the present implementation returns the maximum of -`64 kB` and `minimum_size()`.]] +`128 kB` and `minimum_size()`.]] [[Throws:] [Nothing.]] ] diff --git a/src/asm/jump_i386_sysv_elf_gas.S b/src/asm/jump_i386_sysv_elf_gas.S index 47be9e77..ed83717c 100644 --- a/src/asm/jump_i386_sysv_elf_gas.S +++ b/src/asm/jump_i386_sysv_elf_gas.S @@ -24,6 +24,10 @@ * * ****************************************************************************************/ +#ifdef __x86_64__ +#include "jump_x86_64_sysv_elf_gas.S" +#else + .file "jump_i386_sysv_elf_gas.S" .text .globl jump_fcontext @@ -91,3 +95,5 @@ jump_fcontext: /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits + +#endif diff --git a/src/asm/jump_sparc64_sysv_elf_gas.S b/src/asm/jump_sparc64_sysv_elf_gas.S new file mode 100644 index 00000000..61101fb1 --- /dev/null +++ b/src/asm/jump_sparc64_sysv_elf_gas.S @@ -0,0 +1,51 @@ +/* + Copyright Claudio Jeker 2024 + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/* + * typedef void* fcontext_t; + * + * struct transfer_t { + * fcontext_t fctx; + * void * data; + * }; + * + * transfer_t jump_fcontext(fcontext_t const to, void *vp); + */ +#define CC64FSZ 176 +#define BIAS 2047 +#define SP 128 +#define I7 136 + +.file "jump_sparc64_sysv_elf_gas.S" +.text +.align 4 +.global jump_fcontext +.type jump_fcontext, %function +jump_fcontext: + # prepare stack + save %sp, -CC64FSZ, %sp + + # store framepointer and return address in slots reserved + # for arguments + stx %fp, [%sp + BIAS + SP] + stx %i7, [%sp + BIAS + I7] + mov %sp, %o0 + # force flush register windows to stack and with that save context + flushw + # get SP (pointing to new context-data) from %i0 param + mov %i0, %sp + # load framepointer and return address from context + ldx [%sp + BIAS + SP], %fp + ldx [%sp + BIAS + I7], %i7 + + ret + restore %o0, %g0, %o0 + # restore old %sp (pointing to old context-data) in %o0 + # *data stored in %o1 was not modified +.size jump_fcontext,.-jump_fcontext +# Mark that we don't need executable stack. +.section .note.GNU-stack,"",%progbits diff --git a/src/asm/jump_x86_64_sysv_elf_gas.S b/src/asm/jump_x86_64_sysv_elf_gas.S index 58f0e241..be264bdc 100644 --- a/src/asm/jump_x86_64_sysv_elf_gas.S +++ b/src/asm/jump_x86_64_sysv_elf_gas.S @@ -31,13 +31,16 @@ * * ****************************************************************************************/ -# if defined __CET__ -# include -# define SHSTK_ENABLED (__CET__ & 0x2) -# define BOOST_CONTEXT_SHADOW_STACK (SHSTK_ENABLED && SHADOW_STACK_SYSCALL) +# ifdef __i386__ +# include "jump_i386_sysv_elf_gas.S" # else -# define _CET_ENDBR -# endif +# if defined __CET__ +# include +# define SHSTK_ENABLED (__CET__ & 0x2) +# define BOOST_CONTEXT_SHADOW_STACK (SHSTK_ENABLED && SHADOW_STACK_SYSCALL) +# else +# define _CET_ENDBR +# endif .file "jump_x86_64_sysv_elf_gas.S" .text .globl jump_fcontext @@ -72,6 +75,14 @@ jump_fcontext: movq %rcx, (%rsp) #endif +#if BOOST_CONTEXT_SHADOW_STACK + /* grow the stack to reserve space for shadow stack pointer(SSP) */ + leaq -0x8(%rsp), %rsp + /* read the current SSP and store it */ + rdsspq %rcx + movq %rcx, (%rsp) +# endif + /* store RSP (pointing to context-data) in RAX */ movq %rsp, %rax @@ -140,3 +151,4 @@ jump_fcontext: /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits +# endif diff --git a/src/asm/make_i386_sysv_elf_gas.S b/src/asm/make_i386_sysv_elf_gas.S index 9261e566..c6e0b365 100644 --- a/src/asm/make_i386_sysv_elf_gas.S +++ b/src/asm/make_i386_sysv_elf_gas.S @@ -24,6 +24,10 @@ * * ****************************************************************************************/ +#ifdef __x86_64__ +#include "make_x86_64_sysv_elf_gas.S" +#else + .file "make_i386_sysv_elf_gas.S" .text .globl make_fcontext @@ -111,3 +115,5 @@ finish: /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits + +#endif diff --git a/src/asm/make_sparc64_sysv_elf_gas.S b/src/asm/make_sparc64_sysv_elf_gas.S new file mode 100644 index 00000000..3e7ee809 --- /dev/null +++ b/src/asm/make_sparc64_sysv_elf_gas.S @@ -0,0 +1,68 @@ +/* + Copyright Claudio Jeker 2024 + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/* + * fcontext_t *make_fcontext(void *sp, size_t size, void (*fn)(transfer_t)); + */ +#define CC64FSZ 176 +#define BIAS 2047 +#define FP 112 +#define SP 128 +#define I7 136 + +.file "make_sparc64_sysv_elf_gas.S" +.text +.align 4 +.global make_fcontext +.type make_fcontext, %function +make_fcontext: + save %sp, -CC64FSZ, %sp + + # shift address in %i0 (allocated stack) to lower 16 byte boundary + and %i0, -0xf, %i0 + + # reserve space for two frames on the stack + # the first frame is for the call the second one holds the data + # for jump_fcontext + sub %i0, 2 * CC64FSZ, %i0 + + # third argument of make_fcontext() is the context-function to call + # store it in the first stack frame, also clear %fp there to indicate + # the end of the stack. + stx %i2, [%i0 + CC64FSZ + I7] + stx %g0, [%i0 + CC64FSZ + FP] + + # On OpenBSD stackghost prevents overriding the return address on + # a stack frame. So this code uses an extra trampoline to load + # to call the context-function and then do the _exit(0) dance. + # Extract the full address of the trampoline via pc relative addressing +1: + rd %pc, %l0 + add %l0, (trampoline - 1b - 8), %l0 + stx %l0, [%i0 + I7] + + # Save framepointer to first stack frame but first substract the BIAS + add %i0, CC64FSZ - BIAS, %l0 + stx %l0, [%i0 + SP] + + # Return context-data which is also includes the BIAS + ret + restore %i0, -BIAS, %o0 + +trampoline: + ldx [%sp + BIAS + I7], %l0 + + # no need to setup transfer_t, already in %o0 and %o1 + jmpl %l0, %o7 + nop + + call _exit + clr %o0 + unimp +.size make_fcontext,.-make_fcontext +# Mark that we don't need executable stack. +.section .note.GNU-stack,"",%progbits diff --git a/src/asm/make_x86_64_sysv_elf_gas.S b/src/asm/make_x86_64_sysv_elf_gas.S index 4294398a..b0d0c034 100644 --- a/src/asm/make_x86_64_sysv_elf_gas.S +++ b/src/asm/make_x86_64_sysv_elf_gas.S @@ -31,13 +31,16 @@ * * ****************************************************************************************/ -# if defined __CET__ -# include -# define SHSTK_ENABLED (__CET__ & 0x2) -# define BOOST_CONTEXT_SHADOW_STACK (SHSTK_ENABLED && SHADOW_STACK_SYSCALL) +# ifdef __i386__ +# include "make_i386_sysv_elf_gas.S" # else -# define _CET_ENDBR -# endif +# if defined __CET__ +# include +# define SHSTK_ENABLED (__CET__ & 0x2) +# define BOOST_CONTEXT_SHADOW_STACK (SHSTK_ENABLED && SHADOW_STACK_SYSCALL) +# else +# define _CET_ENDBR +# endif .file "make_x86_64_sysv_elf_gas.S" .text .globl make_fcontext @@ -45,6 +48,7 @@ .align 16 make_fcontext: _CET_ENDBR + #if BOOST_CONTEXT_SHADOW_STACK /* the new shadow stack pointer (SSP) */ movq -0x8(%rdi), %r9 @@ -116,12 +120,50 @@ make_fcontext: movq %r9, (%rax) #endif +#if BOOST_CONTEXT_SHADOW_STACK + /* Populate the shadow stack */ + + /* get original SSP */ + rdsspq %r8 + /* restore new shadow stack */ + rstorssp -0x8(%r9) + /* save the restore token on the original shadow stack */ + saveprevssp + /* push the address of "jmp trampoline" to the new shadow stack */ + /* as well as the stack */ + call 1f + jmp trampoline +1: + /* save address of "jmp trampoline" as return-address */ + /* for context-function */ + pop 0x38(%rax) + /* Get the new SSP. */ + rdsspq %r9 + /* restore original shadow stack */ + rstorssp -0x8(%r8) + /* save the restore token on the new shadow stack. */ + saveprevssp + + /* now the new shadow stack looks like: + base-> +------------------------------+ + | address of "jmp trampoline" | + SSP-> +------------------------------+ + | restore token | + +------------------------------+ + */ + + /* reserve space for the new SSP */ + leaq -0x8(%rax), %rax + /* save the new SSP to this fcontext */ + movq %r9, (%rax) +#endif + ret /* return pointer to context-data */ trampoline: - _CET_ENDBR /* store return address on stack */ /* fix stack alignment */ + _CET_ENDBR #if BOOST_CONTEXT_SHADOW_STACK /* save address of "jmp *%rbp" as return-address */ /* on stack and shadow stack */ @@ -145,3 +187,4 @@ finish: /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits +# endif diff --git a/src/asm/ontop_sparc64_sysv_elf_gas.S b/src/asm/ontop_sparc64_sysv_elf_gas.S new file mode 100644 index 00000000..2fcdb891 --- /dev/null +++ b/src/asm/ontop_sparc64_sysv_elf_gas.S @@ -0,0 +1,50 @@ +/* + Copyright Claudio Jeker 2024 + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/* + * transfer_t ontop_fcontext(fcontext_t const to, void *vp, transfer_t (*fn)(transfer_t)); + */ +#define CC64FSZ 176 +#define BIAS 2047 +#define SP 128 +#define I7 136 + +.file "ontop_sparc64_sysv_elf_gas.S" +.text +.align 4 +.global ontop_fcontext +.type ontop_fcontext, %function +ontop_fcontext: + # prepare stack + save %sp, -CC64FSZ, %sp + + # store framepointer and return address in slots reserved + # for arguments + stx %fp, [%sp + BIAS + SP] + stx %i7, [%sp + BIAS + I7] + mov %sp, %o0 + # force flush register windows to stack and with that save context + flushw + # get SP (pointing to new context-data) from %i0 param + mov %i0, %sp + # load framepointer and return address from context + ldx [%sp + BIAS + SP], %fp + ldx [%sp + BIAS + I7], %i7 + + # ontop_fcontext requires to directly call a function on top of the + # current frame so restore register window before doing the jump + # to the context function which then is in %o2. Do not clobber + # %o7 in the jump so that (*fn)() returns to that address. + restore %o0, %g0, %o0 + # restore old %sp (pointing to old context-data) in %o0 + # *data stored in %o1 was not modified + + jmpl %o2, %g0 + nop +.size jump_fcontext,.-jump_fcontext +# Mark that we don't need executable stack. +.section .note.GNU-stack,"",%progbits diff --git a/test/test_fcontext.cpp b/test/test_fcontext.cpp index 51732426..6bbd9ed6 100644 --- a/test/test_fcontext.cpp +++ b/test/test_fcontext.cpp @@ -23,6 +23,12 @@ #include #include +#if defined(BOOST_CONTEXT_USE_MAP_STACK) +extern "C" { +#include +} +#endif + #define BOOST_CHECK(x) BOOST_TEST(x) #define BOOST_CHECK_EQUAL(a, b) BOOST_TEST_EQ(a, b) @@ -44,8 +50,17 @@ class simple_stack_allocator BOOST_ASSERT( minimum_stacksize() <= size); BOOST_ASSERT( maximum_stacksize() >= size); - void * limit = malloc( size); - if ( ! limit) throw std::bad_alloc(); +#if defined(BOOST_CONTEXT_USE_MAP_STACK) + void * limit = ::mmap( 0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_STACK, -1, 0); + if ( limit == MAP_FAILED) { + throw std::bad_alloc(); + } +#else + void * limit = std::malloc( size); + if ( ! limit) { + throw std::bad_alloc(); + } +#endif return static_cast< char * >( limit) + size; } @@ -57,7 +72,11 @@ class simple_stack_allocator BOOST_ASSERT( maximum_stacksize() >= size); void * limit = static_cast< char * >( vp) - size; +#if defined(BOOST_CONTEXT_USE_MAP_STACK) + ::munmap( vp, size); +#else free( limit); +#endif } };