diff options
Diffstat (limited to 'coroutine/arm64')
| -rw-r--r-- | coroutine/arm64/Context.S | 160 | ||||
| -rw-r--r-- | coroutine/arm64/Context.asm | 81 | ||||
| -rw-r--r-- | coroutine/arm64/Context.h | 118 |
3 files changed, 359 insertions, 0 deletions
diff --git a/coroutine/arm64/Context.S b/coroutine/arm64/Context.S new file mode 100644 index 0000000000..ce219c0c4d --- /dev/null +++ b/coroutine/arm64/Context.S @@ -0,0 +1,160 @@ +## +## This file is part of the "Coroutine" project and released under the MIT License. +## +## Created by Samuel Williams on 10/5/2018. +## Copyright, 2018, by Samuel Williams. +## + +#define TOKEN_PASTE(x,y) x##y + +#if defined(__APPLE__) +#define x29 fp +#define x30 lr +.text +.p2align 2 +#else +.text +.align 2 +#endif + +#if defined(__ARM_FEATURE_PAC_DEFAULT) && (__ARM_FEATURE_PAC_DEFAULT & 0x02) != 0 +# error "-mbranch-protection flag specified b-key but Context.S does not support this" +#endif + +#if defined(_WIN32) +## Add more space for certain TEB values on each stack +#define TEB_OFFSET 0x20 +#else +#define TEB_OFFSET 0x00 +#endif + +## NOTE(PAC): Use we HINT mnemonics instead of PAC mnemonics to +## keep compatibility with those assemblers that don't support PAC. +## +## See "Providing protection for complex software" for more details about PAC/BTI +## https://developer.arm.com/architectures/learn-the-architecture/providing-protection-for-complex-software + +.global PREFIXED_SYMBOL(coroutine_transfer) +PREFIXED_SYMBOL(coroutine_transfer): + +#if defined(__ARM_FEATURE_PAC_DEFAULT) && (__ARM_FEATURE_PAC_DEFAULT != 0) + # paciasp (it also acts as BTI landing pad, so no need to insert BTI also) + hint #25 +#elif defined(__ARM_FEATURE_BTI_DEFAULT) && (__ARM_FEATURE_BTI_DEFAULT != 0) + # For the case PAC is not enabled but BTI is. + # bti c + hint #34 +#endif + # Make space on the stack for caller registers + sub sp, sp, 0xa0 + TEB_OFFSET + + # Save caller registers + stp d8, d9, [sp, 0x00 + TEB_OFFSET] + stp d10, d11, [sp, 0x10 + TEB_OFFSET] + stp d12, d13, [sp, 0x20 + TEB_OFFSET] + stp d14, d15, [sp, 0x30 + TEB_OFFSET] + stp x19, x20, [sp, 0x40 + TEB_OFFSET] + stp x21, x22, [sp, 0x50 + TEB_OFFSET] + stp x23, x24, [sp, 0x60 + TEB_OFFSET] + stp x25, x26, [sp, 0x70 + TEB_OFFSET] + stp x27, x28, [sp, 0x80 + TEB_OFFSET] + stp x29, x30, [sp, 0x90 + TEB_OFFSET] + +#if defined(_WIN32) + # Save certain values from Thread Environment Block (TEB) + # x18 points to the TEB on Windows + # Read TeStackBase and TeStackLimit at ksarm64.h from TEB + ldp x5, x6, [x18, #0x08] + # Save them + stp x5, x6, [sp, #0x00] + # Read TeDeallocationStack at ksarm64.h from TEB + ldr x5, [x18, #0x1478] + # Read TeFiberData at ksarm64.h from TEB + ldr x6, [x18, #0x20] + # Save current fiber data and deallocation stack + stp x5, x6, [sp, #0x10] +#endif + + # Save stack pointer to x0 (first argument) + mov x2, sp + str x2, [x0, 0] + + # Load stack pointer from x1 (second argument) + ldr x3, [x1, 0] + mov sp, x3 + +#if defined(_WIN32) + # Restore stack base and limit + ldp x5, x6, [sp, #0x00] + # Write TeStackBase and TeStackLimit at ksarm64.h to TEB + stp x5, x6, [x18, #0x08] + # Restore fiber data and deallocation stack + ldp x5, x6, [sp, #0x10] + # Write TeDeallocationStack at ksarm64.h to TEB + str x5, [x18, #0x1478] + # Write TeFiberData at ksarm64.h to TEB + str x6, [x18, #0x20] +#endif + + # Restore caller registers + ldp d8, d9, [sp, 0x00 + TEB_OFFSET] + ldp d10, d11, [sp, 0x10 + TEB_OFFSET] + ldp d12, d13, [sp, 0x20 + TEB_OFFSET] + ldp d14, d15, [sp, 0x30 + TEB_OFFSET] + ldp x19, x20, [sp, 0x40 + TEB_OFFSET] + ldp x21, x22, [sp, 0x50 + TEB_OFFSET] + ldp x23, x24, [sp, 0x60 + TEB_OFFSET] + ldp x25, x26, [sp, 0x70 + TEB_OFFSET] + ldp x27, x28, [sp, 0x80 + TEB_OFFSET] + ldp x29, x30, [sp, 0x90 + TEB_OFFSET] + + # Pop stack frame + add sp, sp, 0xa0 + TEB_OFFSET + +#if defined(__ARM_FEATURE_PAC_DEFAULT) && (__ARM_FEATURE_PAC_DEFAULT != 0) + # autiasp: Authenticate x30 (LR) with SP and key A + hint #29 +#endif + + # Jump to return address (in x30) + ret + +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif + +#if (defined(__ARM_FEATURE_BTI_DEFAULT) && __ARM_FEATURE_BTI_DEFAULT != 0) || (defined(__ARM_FEATURE_PAC_DEFAULT) && __ARM_FEATURE_PAC_DEFAULT != 0) +#if defined(__ELF__) +/* See "ELF for the Arm 64-bit Architecture (AArch64)" + https://github.com/ARM-software/abi-aa/blob/2023Q3/aaelf64/aaelf64.rst#program-property */ +# define GNU_PROPERTY_AARCH64_FEATURE_1_BTI (1<<0) +# define GNU_PROPERTY_AARCH64_FEATURE_1_PAC (1<<1) + +# if defined(__ARM_FEATURE_BTI_DEFAULT) && __ARM_FEATURE_BTI_DEFAULT != 0 +# define BTI_FLAG GNU_PROPERTY_AARCH64_FEATURE_1_BTI +# else +# define BTI_FLAG 0 +# endif +# if defined(__ARM_FEATURE_PAC_DEFAULT) && __ARM_FEATURE_PAC_DEFAULT != 0 +# define PAC_FLAG GNU_PROPERTY_AARCH64_FEATURE_1_PAC +# else +# define PAC_FLAG 0 +# endif + + # The note section format is described by Note Section in Chapter 5 + # of "System V Application Binary Interface, Edition 4.1". + .pushsection .note.gnu.property, "a" + .p2align 3 + .long 0x4 /* Name size ("GNU\0") */ + .long 0x10 /* Descriptor size */ + .long 0x5 /* Type: NT_GNU_PROPERTY_TYPE_0 */ + .asciz "GNU" /* Name */ + # Begin descriptor + .long 0xc0000000 /* Property type: GNU_PROPERTY_AARCH64_FEATURE_1_AND */ + .long 0x4 /* Property size */ + .long (BTI_FLAG|PAC_FLAG) + .long 0x0 /* 8-byte alignment padding */ + # End descriptor + .popsection +#endif +#endif diff --git a/coroutine/arm64/Context.asm b/coroutine/arm64/Context.asm new file mode 100644 index 0000000000..866fa628e7 --- /dev/null +++ b/coroutine/arm64/Context.asm @@ -0,0 +1,81 @@ + TTL coroutine/arm64/Context.asm + + AREA |.drectve|, DRECTVE + + EXPORT |coroutine_transfer| + + AREA |.text$mn|, CODE, ARM64 + +;; Add more space for certain TEB values on each stack +TEB_OFFSET EQU 0x20 + +;; Incomplete implementation +coroutine_transfer PROC + ; Make space on the stack for caller registers + sub sp, sp, 0xa0 + TEB_OFFSET + + ; Save caller registers + stp d8, d9, [sp, 0x00 + TEB_OFFSET] + stp d10, d11, [sp, 0x10 + TEB_OFFSET] + stp d12, d13, [sp, 0x20 + TEB_OFFSET] + stp d14, d15, [sp, 0x30 + TEB_OFFSET] + stp x19, x20, [sp, 0x40 + TEB_OFFSET] + stp x21, x22, [sp, 0x50 + TEB_OFFSET] + stp x23, x24, [sp, 0x60 + TEB_OFFSET] + stp x25, x26, [sp, 0x70 + TEB_OFFSET] + stp x27, x28, [sp, 0x80 + TEB_OFFSET] + stp x29, x30, [sp, 0x90 + TEB_OFFSET] + + ;; Save certain values from Thread Environment Block (TEB) x18 + ;; points to the TEB on Windows + ;; Read TeStackBase and TeStackLimit at ksarm64.h from TEB + ldp x5, x6, [x18, #0x08] + ;; Save them + stp x5, x6, [sp, #0x00] + ;; Read TeDeallocationStack at ksarm64.h from TEB + ldr x5, [x18, #0x1478] + ;; Read TeFiberData at ksarm64.h from TEB + ldr x6, [x18, #0x20] + ;; Save current fiber data and deallocation stack + stp x5, x6, [sp, #0x10] + + ; Save stack pointer to x0 (first argument) + mov x2, sp + str x2, [x0, 0] + + ; Load stack pointer from x1 (second argument) + ldr x3, [x1, 0] + mov sp, x3 + + ;; Restore stack base and limit + ldp x5, x6, [sp, #0x00] + ;; Write TeStackBase and TeStackLimit at ksarm64.h to TEB + stp x5, x6, [x18, #0x08] + ;; Restore fiber data and deallocation stack + ldp x5, x6, [sp, #0x10] + ;; Write TeDeallocationStack at ksarm64.h to TEB + str x5, [x18, #0x1478] + ;; Write TeFiberData at ksarm64.h to TEB + str x6, [x18, #0x20] + + ; Restore caller registers + ldp d8, d9, [sp, 0x00 + TEB_OFFSET] + ldp d10, d11, [sp, 0x10 + TEB_OFFSET] + ldp d12, d13, [sp, 0x20 + TEB_OFFSET] + ldp d14, d15, [sp, 0x30 + TEB_OFFSET] + ldp x19, x20, [sp, 0x40 + TEB_OFFSET] + ldp x21, x22, [sp, 0x50 + TEB_OFFSET] + ldp x23, x24, [sp, 0x60 + TEB_OFFSET] + ldp x25, x26, [sp, 0x70 + TEB_OFFSET] + ldp x27, x28, [sp, 0x80 + TEB_OFFSET] + ldp x29, x30, [sp, 0x90 + TEB_OFFSET] + + ; Pop stack frame + add sp, sp, 0xa0 + TEB_OFFSET + + ; Jump to return address (in x30) + ret + + endp + + end diff --git a/coroutine/arm64/Context.h b/coroutine/arm64/Context.h new file mode 100644 index 0000000000..468e4155b2 --- /dev/null +++ b/coroutine/arm64/Context.h @@ -0,0 +1,118 @@ +#ifndef COROUTINE_ARM64_CONTEXT_H +#define COROUTINE_ARM64_CONTEXT_H 1 + +/* + * This file is part of the "Coroutine" project and released under the MIT License. + * + * Created by Samuel Williams on 10/5/2018. + * Copyright, 2018, by Samuel Williams. +*/ + +#pragma once + +#include <assert.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#if defined __GNUC__ +#define COROUTINE __attribute__((noreturn)) void +#define COROUTINE_DECL COROUTINE +#elif defined _MSC_VER +#define COROUTINE __declspec(noreturn) void +#define COROUTINE_DECL void +#endif + +#if defined(_WIN32) +#define TEB_OFFSET 0x20 +#else +#define TEB_OFFSET 0x00 +#endif + +enum {COROUTINE_REGISTERS = (0xa0 + TEB_OFFSET) / 8}; + +#if defined(__SANITIZE_ADDRESS__) + #define COROUTINE_SANITIZE_ADDRESS +#elif defined(__has_feature) + #if __has_feature(address_sanitizer) + #define COROUTINE_SANITIZE_ADDRESS + #endif +#endif + +#if defined(COROUTINE_SANITIZE_ADDRESS) +#include <sanitizer/common_interface_defs.h> +#include <sanitizer/asan_interface.h> +#endif + +struct coroutine_context +{ + void **stack_pointer; + void *argument; + +#if defined(COROUTINE_SANITIZE_ADDRESS) + void *fake_stack; + void *stack_base; + size_t stack_size; +#endif +}; + +typedef COROUTINE_DECL(* coroutine_start)(struct coroutine_context *from, struct coroutine_context *self); + +static inline void coroutine_initialize_main(struct coroutine_context * context) { + context->stack_pointer = NULL; +} + +static inline void *ptrauth_sign_instruction_addr(void *addr, void *modifier) { +#if defined(__ARM_FEATURE_PAC_DEFAULT) && __ARM_FEATURE_PAC_DEFAULT != 0 + // Sign the given instruction address with the given modifier and key A + register void *r17 __asm("r17") = addr; + register void *r16 __asm("r16") = modifier; + // Use HINT mnemonic instead of PACIA1716 for compatibility with older assemblers. + __asm ("hint #8;" : "+r"(r17) : "r"(r16)); + addr = r17; +#else + // No-op if PAC is not enabled +#endif + return addr; +} + +static inline void coroutine_initialize( + struct coroutine_context *context, + coroutine_start start, + void *stack, + size_t size +) { + assert(start && stack && size >= 1024); + +#if defined(COROUTINE_SANITIZE_ADDRESS) + context->fake_stack = NULL; + context->stack_base = stack; + context->stack_size = size; +#endif + + // Stack grows down. Force 16-byte alignment. + char * top = (char*)stack + size; + top = (char *)((uintptr_t)top & ~0xF); + context->stack_pointer = (void**)top; + + context->stack_pointer -= COROUTINE_REGISTERS; + memset(context->stack_pointer, 0, sizeof(void*) * COROUTINE_REGISTERS); + + void *addr = (void*)(uintptr_t)start; + context->stack_pointer[(0x98 + TEB_OFFSET) / 8] = ptrauth_sign_instruction_addr(addr, (void*)top); +#if defined(_WIN32) + // save top address of stack as base in TEB + context->stack_pointer[0x00 / 8] = (char*)stack + size; + // save botton address of stack as limit and deallocation stack in TEB + context->stack_pointer[0x08 / 8] = stack; + context->stack_pointer[0x10 / 8] = stack; +#endif +} + +struct coroutine_context * coroutine_transfer(struct coroutine_context * current, struct coroutine_context * target); + +static inline void coroutine_destroy(struct coroutine_context * context) +{ +} + +#endif /* COROUTINE_ARM64_CONTEXT_H */ |
