1 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */ 1 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */ 2 /* 2 /* 3 * x86_64 specific definitions for NOLIBC 3 * x86_64 specific definitions for NOLIBC 4 * Copyright (C) 2017-2022 Willy Tarreau <w@1w 4 * Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu> 5 */ 5 */ 6 6 7 #ifndef _NOLIBC_ARCH_X86_64_H 7 #ifndef _NOLIBC_ARCH_X86_64_H 8 #define _NOLIBC_ARCH_X86_64_H 8 #define _NOLIBC_ARCH_X86_64_H 9 9 10 #include "compiler.h" 10 #include "compiler.h" 11 #include "crt.h" 11 #include "crt.h" 12 12 13 /* Syscalls for x86_64 : 13 /* Syscalls for x86_64 : 14 * - registers are 64-bit 14 * - registers are 64-bit 15 * - syscall number is passed in rax 15 * - syscall number is passed in rax 16 * - arguments are in rdi, rsi, rdx, r10, r8 16 * - arguments are in rdi, rsi, rdx, r10, r8, r9 respectively 17 * - the system call is performed by calling 17 * - the system call is performed by calling the syscall instruction 18 * - syscall return comes in rax 18 * - syscall return comes in rax 19 * - rcx and r11 are clobbered, others are p 19 * - rcx and r11 are clobbered, others are preserved. 20 * - the arguments are cast to long and assi 20 * - the arguments are cast to long and assigned into the target registers 21 * which are then simply passed as registe 21 * which are then simply passed as registers to the asm code, so that we 22 * don't have to experience issues with re 22 * don't have to experience issues with register constraints. 23 * - the syscall number is always specified 23 * - the syscall number is always specified last in order to allow to force 24 * some registers before (gcc refuses a %- 24 * some registers before (gcc refuses a %-register at the last position). 25 * - see also x86-64 ABI section A.2 AMD64 L 25 * - see also x86-64 ABI section A.2 AMD64 Linux Kernel Conventions, A.2.1 26 * Calling Conventions. 26 * Calling Conventions. 27 * 27 * 28 * Link x86-64 ABI: https://gitlab.com/x86-psA 28 * Link x86-64 ABI: https://gitlab.com/x86-psABIs/x86-64-ABI/-/wikis/home 29 * 29 * 30 */ 30 */ 31 31 32 #define my_syscall0(num) 32 #define my_syscall0(num) \ 33 ({ 33 ({ \ 34 long _ret; 34 long _ret; \ 35 register long _num __asm__ ("rax") = 35 register long _num __asm__ ("rax") = (num); \ 36 36 \ 37 __asm__ volatile ( 37 __asm__ volatile ( \ 38 "syscall\n" 38 "syscall\n" \ 39 : "=a"(_ret) 39 : "=a"(_ret) \ 40 : ""(_num) 40 : ""(_num) \ 41 : "rcx", "r11", "memory", "cc" 41 : "rcx", "r11", "memory", "cc" \ 42 ); 42 ); \ 43 _ret; 43 _ret; \ 44 }) 44 }) 45 45 46 #define my_syscall1(num, arg1) 46 #define my_syscall1(num, arg1) \ 47 ({ 47 ({ \ 48 long _ret; 48 long _ret; \ 49 register long _num __asm__ ("rax") = 49 register long _num __asm__ ("rax") = (num); \ 50 register long _arg1 __asm__ ("rdi") = 50 register long _arg1 __asm__ ("rdi") = (long)(arg1); \ 51 51 \ 52 __asm__ volatile ( 52 __asm__ volatile ( \ 53 "syscall\n" 53 "syscall\n" \ 54 : "=a"(_ret) 54 : "=a"(_ret) \ 55 : "r"(_arg1), 55 : "r"(_arg1), \ 56 ""(_num) 56 ""(_num) \ 57 : "rcx", "r11", "memory", "cc" 57 : "rcx", "r11", "memory", "cc" \ 58 ); 58 ); \ 59 _ret; 59 _ret; \ 60 }) 60 }) 61 61 62 #define my_syscall2(num, arg1, arg2) 62 #define my_syscall2(num, arg1, arg2) \ 63 ({ 63 ({ \ 64 long _ret; 64 long _ret; \ 65 register long _num __asm__ ("rax") = 65 register long _num __asm__ ("rax") = (num); \ 66 register long _arg1 __asm__ ("rdi") = 66 register long _arg1 __asm__ ("rdi") = (long)(arg1); \ 67 register long _arg2 __asm__ ("rsi") = 67 register long _arg2 __asm__ ("rsi") = (long)(arg2); \ 68 68 \ 69 __asm__ volatile ( 69 __asm__ volatile ( \ 70 "syscall\n" 70 "syscall\n" \ 71 : "=a"(_ret) 71 : "=a"(_ret) \ 72 : "r"(_arg1), "r"(_arg2), 72 : "r"(_arg1), "r"(_arg2), \ 73 ""(_num) 73 ""(_num) \ 74 : "rcx", "r11", "memory", "cc" 74 : "rcx", "r11", "memory", "cc" \ 75 ); 75 ); \ 76 _ret; 76 _ret; \ 77 }) 77 }) 78 78 79 #define my_syscall3(num, arg1, arg2, arg3) 79 #define my_syscall3(num, arg1, arg2, arg3) \ 80 ({ 80 ({ \ 81 long _ret; 81 long _ret; \ 82 register long _num __asm__ ("rax") = 82 register long _num __asm__ ("rax") = (num); \ 83 register long _arg1 __asm__ ("rdi") = 83 register long _arg1 __asm__ ("rdi") = (long)(arg1); \ 84 register long _arg2 __asm__ ("rsi") = 84 register long _arg2 __asm__ ("rsi") = (long)(arg2); \ 85 register long _arg3 __asm__ ("rdx") = 85 register long _arg3 __asm__ ("rdx") = (long)(arg3); \ 86 86 \ 87 __asm__ volatile ( 87 __asm__ volatile ( \ 88 "syscall\n" 88 "syscall\n" \ 89 : "=a"(_ret) 89 : "=a"(_ret) \ 90 : "r"(_arg1), "r"(_arg2), "r"( 90 : "r"(_arg1), "r"(_arg2), "r"(_arg3), \ 91 ""(_num) 91 ""(_num) \ 92 : "rcx", "r11", "memory", "cc" 92 : "rcx", "r11", "memory", "cc" \ 93 ); 93 ); \ 94 _ret; 94 _ret; \ 95 }) 95 }) 96 96 97 #define my_syscall4(num, arg1, arg2, arg3, arg 97 #define my_syscall4(num, arg1, arg2, arg3, arg4) \ 98 ({ 98 ({ \ 99 long _ret; 99 long _ret; \ 100 register long _num __asm__ ("rax") = 100 register long _num __asm__ ("rax") = (num); \ 101 register long _arg1 __asm__ ("rdi") = 101 register long _arg1 __asm__ ("rdi") = (long)(arg1); \ 102 register long _arg2 __asm__ ("rsi") = 102 register long _arg2 __asm__ ("rsi") = (long)(arg2); \ 103 register long _arg3 __asm__ ("rdx") = 103 register long _arg3 __asm__ ("rdx") = (long)(arg3); \ 104 register long _arg4 __asm__ ("r10") = 104 register long _arg4 __asm__ ("r10") = (long)(arg4); \ 105 105 \ 106 __asm__ volatile ( 106 __asm__ volatile ( \ 107 "syscall\n" 107 "syscall\n" \ 108 : "=a"(_ret) 108 : "=a"(_ret) \ 109 : "r"(_arg1), "r"(_arg2), "r"( 109 : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \ 110 ""(_num) 110 ""(_num) \ 111 : "rcx", "r11", "memory", "cc" 111 : "rcx", "r11", "memory", "cc" \ 112 ); 112 ); \ 113 _ret; 113 _ret; \ 114 }) 114 }) 115 115 116 #define my_syscall5(num, arg1, arg2, arg3, arg 116 #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \ 117 ({ 117 ({ \ 118 long _ret; 118 long _ret; \ 119 register long _num __asm__ ("rax") = 119 register long _num __asm__ ("rax") = (num); \ 120 register long _arg1 __asm__ ("rdi") = 120 register long _arg1 __asm__ ("rdi") = (long)(arg1); \ 121 register long _arg2 __asm__ ("rsi") = 121 register long _arg2 __asm__ ("rsi") = (long)(arg2); \ 122 register long _arg3 __asm__ ("rdx") = 122 register long _arg3 __asm__ ("rdx") = (long)(arg3); \ 123 register long _arg4 __asm__ ("r10") = 123 register long _arg4 __asm__ ("r10") = (long)(arg4); \ 124 register long _arg5 __asm__ ("r8") = 124 register long _arg5 __asm__ ("r8") = (long)(arg5); \ 125 125 \ 126 __asm__ volatile ( 126 __asm__ volatile ( \ 127 "syscall\n" 127 "syscall\n" \ 128 : "=a"(_ret) 128 : "=a"(_ret) \ 129 : "r"(_arg1), "r"(_arg2), "r"( 129 : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ 130 ""(_num) 130 ""(_num) \ 131 : "rcx", "r11", "memory", "cc" 131 : "rcx", "r11", "memory", "cc" \ 132 ); 132 ); \ 133 _ret; 133 _ret; \ 134 }) 134 }) 135 135 136 #define my_syscall6(num, arg1, arg2, arg3, arg 136 #define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \ 137 ({ 137 ({ \ 138 long _ret; 138 long _ret; \ 139 register long _num __asm__ ("rax") = 139 register long _num __asm__ ("rax") = (num); \ 140 register long _arg1 __asm__ ("rdi") = 140 register long _arg1 __asm__ ("rdi") = (long)(arg1); \ 141 register long _arg2 __asm__ ("rsi") = 141 register long _arg2 __asm__ ("rsi") = (long)(arg2); \ 142 register long _arg3 __asm__ ("rdx") = 142 register long _arg3 __asm__ ("rdx") = (long)(arg3); \ 143 register long _arg4 __asm__ ("r10") = 143 register long _arg4 __asm__ ("r10") = (long)(arg4); \ 144 register long _arg5 __asm__ ("r8") = 144 register long _arg5 __asm__ ("r8") = (long)(arg5); \ 145 register long _arg6 __asm__ ("r9") = 145 register long _arg6 __asm__ ("r9") = (long)(arg6); \ 146 146 \ 147 __asm__ volatile ( 147 __asm__ volatile ( \ 148 "syscall\n" 148 "syscall\n" \ 149 : "=a"(_ret) 149 : "=a"(_ret) \ 150 : "r"(_arg1), "r"(_arg2), "r"( 150 : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ 151 "r"(_arg6), ""(_num) 151 "r"(_arg6), ""(_num) \ 152 : "rcx", "r11", "memory", "cc" 152 : "rcx", "r11", "memory", "cc" \ 153 ); 153 ); \ 154 _ret; 154 _ret; \ 155 }) 155 }) 156 156 157 /* startup code */ 157 /* startup code */ 158 /* 158 /* 159 * x86-64 System V ABI mandates: 159 * x86-64 System V ABI mandates: 160 * 1) %rsp must be 16-byte aligned right befor 160 * 1) %rsp must be 16-byte aligned right before the function call. 161 * 2) The deepest stack frame should be zero ( 161 * 2) The deepest stack frame should be zero (the %rbp). 162 * 162 * 163 */ 163 */ 164 void __attribute__((weak, noreturn)) __nolibc_ 164 void __attribute__((weak, noreturn)) __nolibc_entrypoint __no_stack_protector _start(void) 165 { 165 { 166 __asm__ volatile ( 166 __asm__ volatile ( 167 "xor %ebp, %ebp\n" /* z 167 "xor %ebp, %ebp\n" /* zero the stack frame */ 168 "mov %rsp, %rdi\n" /* s 168 "mov %rsp, %rdi\n" /* save stack pointer to %rdi, as arg1 of _start_c */ 169 "and $-16, %rsp\n" /* % 169 "and $-16, %rsp\n" /* %rsp must be 16-byte aligned before call */ 170 "call _start_c\n" /* t 170 "call _start_c\n" /* transfer to c runtime */ 171 "hlt\n" /* e 171 "hlt\n" /* ensure it does not return */ 172 ); 172 ); 173 __nolibc_entrypoint_epilogue(); 173 __nolibc_entrypoint_epilogue(); 174 } 174 } 175 175 176 #define NOLIBC_ARCH_HAS_MEMMOVE 176 #define NOLIBC_ARCH_HAS_MEMMOVE 177 void *memmove(void *dst, const void *src, size 177 void *memmove(void *dst, const void *src, size_t len); 178 178 179 #define NOLIBC_ARCH_HAS_MEMCPY 179 #define NOLIBC_ARCH_HAS_MEMCPY 180 void *memcpy(void *dst, const void *src, size_ 180 void *memcpy(void *dst, const void *src, size_t len); 181 181 182 #define NOLIBC_ARCH_HAS_MEMSET 182 #define NOLIBC_ARCH_HAS_MEMSET 183 void *memset(void *dst, int c, size_t len); 183 void *memset(void *dst, int c, size_t len); 184 184 185 __asm__ ( 185 __asm__ ( 186 ".section .text.nolibc_memmove_memcpy\n" 186 ".section .text.nolibc_memmove_memcpy\n" 187 ".weak memmove\n" 187 ".weak memmove\n" 188 ".weak memcpy\n" 188 ".weak memcpy\n" 189 "memmove:\n" 189 "memmove:\n" 190 "memcpy:\n" 190 "memcpy:\n" 191 "movq %rdx, %rcx\n\t" 191 "movq %rdx, %rcx\n\t" 192 "movq %rdi, %rax\n\t" 192 "movq %rdi, %rax\n\t" 193 "movq %rdi, %rdx\n\t" 193 "movq %rdi, %rdx\n\t" 194 "subq %rsi, %rdx\n\t" 194 "subq %rsi, %rdx\n\t" 195 "cmpq %rcx, %rdx\n\t" 195 "cmpq %rcx, %rdx\n\t" 196 "jb 1f\n\t" 196 "jb 1f\n\t" 197 "rep movsb\n\t" 197 "rep movsb\n\t" 198 "retq\n" 198 "retq\n" 199 "1:" /* backward copy */ 199 "1:" /* backward copy */ 200 "leaq -1(%rdi, %rcx, 1), %rdi\n\t" 200 "leaq -1(%rdi, %rcx, 1), %rdi\n\t" 201 "leaq -1(%rsi, %rcx, 1), %rsi\n\t" 201 "leaq -1(%rsi, %rcx, 1), %rsi\n\t" 202 "std\n\t" 202 "std\n\t" 203 "rep movsb\n\t" 203 "rep movsb\n\t" 204 "cld\n\t" 204 "cld\n\t" 205 "retq\n" 205 "retq\n" 206 206 207 ".section .text.nolibc_memset\n" 207 ".section .text.nolibc_memset\n" 208 ".weak memset\n" 208 ".weak memset\n" 209 "memset:\n" 209 "memset:\n" 210 "xchgl %eax, %esi\n\t" 210 "xchgl %eax, %esi\n\t" 211 "movq %rdx, %rcx\n\t" 211 "movq %rdx, %rcx\n\t" 212 "pushq %rdi\n\t" 212 "pushq %rdi\n\t" 213 "rep stosb\n\t" 213 "rep stosb\n\t" 214 "popq %rax\n\t" 214 "popq %rax\n\t" 215 "retq\n" 215 "retq\n" 216 ); 216 ); 217 217 218 #endif /* _NOLIBC_ARCH_X86_64_H */ 218 #endif /* _NOLIBC_ARCH_X86_64_H */ 219 219
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.