1 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */ 2 /* 3 * x86_64 specific definitions for NOLIBC 4 * Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu> 5 */ 6 7 #ifndef _NOLIBC_ARCH_X86_64_H 8 #define _NOLIBC_ARCH_X86_64_H 9 10 #include "compiler.h" 11 #include "crt.h" 12 13 /* Syscalls for x86_64 : 14 * - registers are 64-bit 15 * - syscall number is passed in rax 16 * - arguments are in rdi, rsi, rdx, r10, r8, r9 respectively 17 * - the system call is performed by calling the syscall instruction 18 * - syscall return comes in rax 19 * - rcx and r11 are clobbered, others are preserved. 20 * - the arguments are cast to long and assigned into the target registers 21 * which are then simply passed as registers to the asm code, so that we 22 * don't have to experience issues with register constraints. 23 * - the syscall number is always specified last in order to allow to force 24 * some registers before (gcc refuses a %-register at the last position). 25 * - see also x86-64 ABI section A.2 AMD64 Linux Kernel Conventions, A.2.1 26 * Calling Conventions. 27 * 28 * Link x86-64 ABI: https://gitlab.com/x86-psABIs/x86-64-ABI/-/wikis/home 29 * 30 */ 31 32 #define my_syscall0(num) \ 33 ({ \ 34 long _ret; \ 35 register long _num __asm__ ("rax") = (num); \ 36 \ 37 __asm__ volatile ( \ 38 "syscall\n" \ 39 : "=a"(_ret) \ 40 : "0"(_num) \ 41 : "rcx", "r11", "memory", "cc" \ 42 ); \ 43 _ret; \ 44 }) 45 46 #define my_syscall1(num, arg1) \ 47 ({ \ 48 long _ret; \ 49 register long _num __asm__ ("rax") = (num); \ 50 register long _arg1 __asm__ ("rdi") = (long)(arg1); \ 51 \ 52 __asm__ volatile ( \ 53 "syscall\n" \ 54 : "=a"(_ret) \ 55 : "r"(_arg1), \ 56 "0"(_num) \ 57 : "rcx", "r11", "memory", "cc" \ 58 ); \ 59 _ret; \ 60 }) 61 62 #define my_syscall2(num, arg1, arg2) \ 63 ({ \ 64 long _ret; \ 65 register long _num __asm__ ("rax") = (num); \ 66 register long _arg1 __asm__ ("rdi") = (long)(arg1); \ 67 register long _arg2 __asm__ ("rsi") = (long)(arg2); \ 68 \ 69 __asm__ volatile ( \ 70 "syscall\n" \ 71 : "=a"(_ret) \ 72 : "r"(_arg1), "r"(_arg2), \ 73 "0"(_num) \ 74 : "rcx", "r11", "memory", "cc" \ 75 ); \ 76 _ret; \ 77 }) 78 79 #define my_syscall3(num, arg1, arg2, arg3) \ 80 ({ \ 81 long _ret; \ 82 register long _num __asm__ ("rax") = (num); \ 83 register long _arg1 __asm__ ("rdi") = (long)(arg1); \ 84 register long _arg2 __asm__ ("rsi") = (long)(arg2); \ 85 register long _arg3 __asm__ ("rdx") = (long)(arg3); \ 86 \ 87 __asm__ volatile ( \ 88 "syscall\n" \ 89 : "=a"(_ret) \ 90 : "r"(_arg1), "r"(_arg2), "r"(_arg3), \ 91 "0"(_num) \ 92 : "rcx", "r11", "memory", "cc" \ 93 ); \ 94 _ret; \ 95 }) 96 97 #define my_syscall4(num, arg1, arg2, arg3, arg4) \ 98 ({ \ 99 long _ret; \ 100 register long _num __asm__ ("rax") = (num); \ 101 register long _arg1 __asm__ ("rdi") = (long)(arg1); \ 102 register long _arg2 __asm__ ("rsi") = (long)(arg2); \ 103 register long _arg3 __asm__ ("rdx") = (long)(arg3); \ 104 register long _arg4 __asm__ ("r10") = (long)(arg4); \ 105 \ 106 __asm__ volatile ( \ 107 "syscall\n" \ 108 : "=a"(_ret) \ 109 : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \ 110 "0"(_num) \ 111 : "rcx", "r11", "memory", "cc" \ 112 ); \ 113 _ret; \ 114 }) 115 116 #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \ 117 ({ \ 118 long _ret; \ 119 register long _num __asm__ ("rax") = (num); \ 120 register long _arg1 __asm__ ("rdi") = (long)(arg1); \ 121 register long _arg2 __asm__ ("rsi") = (long)(arg2); \ 122 register long _arg3 __asm__ ("rdx") = (long)(arg3); \ 123 register long _arg4 __asm__ ("r10") = (long)(arg4); \ 124 register long _arg5 __asm__ ("r8") = (long)(arg5); \ 125 \ 126 __asm__ volatile ( \ 127 "syscall\n" \ 128 : "=a"(_ret) \ 129 : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ 130 "0"(_num) \ 131 : "rcx", "r11", "memory", "cc" \ 132 ); \ 133 _ret; \ 134 }) 135 136 #define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \ 137 ({ \ 138 long _ret; \ 139 register long _num __asm__ ("rax") = (num); \ 140 register long _arg1 __asm__ ("rdi") = (long)(arg1); \ 141 register long _arg2 __asm__ ("rsi") = (long)(arg2); \ 142 register long _arg3 __asm__ ("rdx") = (long)(arg3); \ 143 register long _arg4 __asm__ ("r10") = (long)(arg4); \ 144 register long _arg5 __asm__ ("r8") = (long)(arg5); \ 145 register long _arg6 __asm__ ("r9") = (long)(arg6); \ 146 \ 147 __asm__ volatile ( \ 148 "syscall\n" \ 149 : "=a"(_ret) \ 150 : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ 151 "r"(_arg6), "0"(_num) \ 152 : "rcx", "r11", "memory", "cc" \ 153 ); \ 154 _ret; \ 155 }) 156 157 /* startup code */ 158 /* 159 * x86-64 System V ABI mandates: 160 * 1) %rsp must be 16-byte aligned right before the function call. 161 * 2) The deepest stack frame should be zero (the %rbp). 162 * 163 */ 164 void __attribute__((weak, noreturn, optimize("Os", "omit-frame-pointer"))) __no_stack_protector _start(void) 165 { 166 __asm__ volatile ( 167 "xor %ebp, %ebp\n" /* zero the stack frame */ 168 "mov %rsp, %rdi\n" /* save stack pointer to %rdi, as arg1 of _start_c */ 169 "and $-16, %rsp\n" /* %rsp must be 16-byte aligned before call */ 170 "call _start_c\n" /* transfer to c runtime */ 171 "hlt\n" /* ensure it does not return */ 172 ); 173 __builtin_unreachable(); 174 } 175 176 #endif /* _NOLIBC_ARCH_X86_64_H */ 177