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 12 /* The struct returned by the stat() syscall, equivalent to stat64(). The 13 * syscall returns 116 bytes and stops in the middle of __unused. 14 */ 15 struct sys_stat_struct { 16 unsigned long st_dev; 17 unsigned long st_ino; 18 unsigned long st_nlink; 19 unsigned int st_mode; 20 unsigned int st_uid; 21 22 unsigned int st_gid; 23 unsigned int __pad0; 24 unsigned long st_rdev; 25 long st_size; 26 long st_blksize; 27 28 long st_blocks; 29 unsigned long st_atime; 30 unsigned long st_atime_nsec; 31 unsigned long st_mtime; 32 33 unsigned long st_mtime_nsec; 34 unsigned long st_ctime; 35 unsigned long st_ctime_nsec; 36 long __unused[3]; 37 }; 38 39 /* Syscalls for x86_64 : 40 * - registers are 64-bit 41 * - syscall number is passed in rax 42 * - arguments are in rdi, rsi, rdx, r10, r8, r9 respectively 43 * - the system call is performed by calling the syscall instruction 44 * - syscall return comes in rax 45 * - rcx and r11 are clobbered, others are preserved. 46 * - the arguments are cast to long and assigned into the target registers 47 * which are then simply passed as registers to the asm code, so that we 48 * don't have to experience issues with register constraints. 49 * - the syscall number is always specified last in order to allow to force 50 * some registers before (gcc refuses a %-register at the last position). 51 * - see also x86-64 ABI section A.2 AMD64 Linux Kernel Conventions, A.2.1 52 * Calling Conventions. 53 * 54 * Link x86-64 ABI: https://gitlab.com/x86-psABIs/x86-64-ABI/-/wikis/home 55 * 56 */ 57 58 #define my_syscall0(num) \ 59 ({ \ 60 long _ret; \ 61 register long _num __asm__ ("rax") = (num); \ 62 \ 63 __asm__ volatile ( \ 64 "syscall\n" \ 65 : "=a"(_ret) \ 66 : "0"(_num) \ 67 : "rcx", "r11", "memory", "cc" \ 68 ); \ 69 _ret; \ 70 }) 71 72 #define my_syscall1(num, arg1) \ 73 ({ \ 74 long _ret; \ 75 register long _num __asm__ ("rax") = (num); \ 76 register long _arg1 __asm__ ("rdi") = (long)(arg1); \ 77 \ 78 __asm__ volatile ( \ 79 "syscall\n" \ 80 : "=a"(_ret) \ 81 : "r"(_arg1), \ 82 "0"(_num) \ 83 : "rcx", "r11", "memory", "cc" \ 84 ); \ 85 _ret; \ 86 }) 87 88 #define my_syscall2(num, arg1, arg2) \ 89 ({ \ 90 long _ret; \ 91 register long _num __asm__ ("rax") = (num); \ 92 register long _arg1 __asm__ ("rdi") = (long)(arg1); \ 93 register long _arg2 __asm__ ("rsi") = (long)(arg2); \ 94 \ 95 __asm__ volatile ( \ 96 "syscall\n" \ 97 : "=a"(_ret) \ 98 : "r"(_arg1), "r"(_arg2), \ 99 "0"(_num) \ 100 : "rcx", "r11", "memory", "cc" \ 101 ); \ 102 _ret; \ 103 }) 104 105 #define my_syscall3(num, arg1, arg2, arg3) \ 106 ({ \ 107 long _ret; \ 108 register long _num __asm__ ("rax") = (num); \ 109 register long _arg1 __asm__ ("rdi") = (long)(arg1); \ 110 register long _arg2 __asm__ ("rsi") = (long)(arg2); \ 111 register long _arg3 __asm__ ("rdx") = (long)(arg3); \ 112 \ 113 __asm__ volatile ( \ 114 "syscall\n" \ 115 : "=a"(_ret) \ 116 : "r"(_arg1), "r"(_arg2), "r"(_arg3), \ 117 "0"(_num) \ 118 : "rcx", "r11", "memory", "cc" \ 119 ); \ 120 _ret; \ 121 }) 122 123 #define my_syscall4(num, arg1, arg2, arg3, arg4) \ 124 ({ \ 125 long _ret; \ 126 register long _num __asm__ ("rax") = (num); \ 127 register long _arg1 __asm__ ("rdi") = (long)(arg1); \ 128 register long _arg2 __asm__ ("rsi") = (long)(arg2); \ 129 register long _arg3 __asm__ ("rdx") = (long)(arg3); \ 130 register long _arg4 __asm__ ("r10") = (long)(arg4); \ 131 \ 132 __asm__ volatile ( \ 133 "syscall\n" \ 134 : "=a"(_ret) \ 135 : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \ 136 "0"(_num) \ 137 : "rcx", "r11", "memory", "cc" \ 138 ); \ 139 _ret; \ 140 }) 141 142 #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \ 143 ({ \ 144 long _ret; \ 145 register long _num __asm__ ("rax") = (num); \ 146 register long _arg1 __asm__ ("rdi") = (long)(arg1); \ 147 register long _arg2 __asm__ ("rsi") = (long)(arg2); \ 148 register long _arg3 __asm__ ("rdx") = (long)(arg3); \ 149 register long _arg4 __asm__ ("r10") = (long)(arg4); \ 150 register long _arg5 __asm__ ("r8") = (long)(arg5); \ 151 \ 152 __asm__ volatile ( \ 153 "syscall\n" \ 154 : "=a"(_ret) \ 155 : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ 156 "0"(_num) \ 157 : "rcx", "r11", "memory", "cc" \ 158 ); \ 159 _ret; \ 160 }) 161 162 #define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \ 163 ({ \ 164 long _ret; \ 165 register long _num __asm__ ("rax") = (num); \ 166 register long _arg1 __asm__ ("rdi") = (long)(arg1); \ 167 register long _arg2 __asm__ ("rsi") = (long)(arg2); \ 168 register long _arg3 __asm__ ("rdx") = (long)(arg3); \ 169 register long _arg4 __asm__ ("r10") = (long)(arg4); \ 170 register long _arg5 __asm__ ("r8") = (long)(arg5); \ 171 register long _arg6 __asm__ ("r9") = (long)(arg6); \ 172 \ 173 __asm__ volatile ( \ 174 "syscall\n" \ 175 : "=a"(_ret) \ 176 : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ 177 "r"(_arg6), "0"(_num) \ 178 : "rcx", "r11", "memory", "cc" \ 179 ); \ 180 _ret; \ 181 }) 182 183 char **environ __attribute__((weak)); 184 const unsigned long *_auxv __attribute__((weak)); 185 186 /* startup code */ 187 /* 188 * x86-64 System V ABI mandates: 189 * 1) %rsp must be 16-byte aligned right before the function call. 190 * 2) The deepest stack frame should be zero (the %rbp). 191 * 192 */ 193 void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) __no_stack_protector _start(void) 194 { 195 __asm__ volatile ( 196 #ifdef _NOLIBC_STACKPROTECTOR 197 "call __stack_chk_init\n" /* initialize stack protector */ 198 #endif 199 "pop %rdi\n" /* argc (first arg, %rdi) */ 200 "mov %rsp, %rsi\n" /* argv[] (second arg, %rsi) */ 201 "lea 8(%rsi,%rdi,8),%rdx\n" /* then a NULL then envp (third arg, %rdx) */ 202 "mov %rdx, environ\n" /* save environ */ 203 "xor %ebp, %ebp\n" /* zero the stack frame */ 204 "mov %rdx, %rax\n" /* search for auxv (follows NULL after last env) */ 205 "0:\n" 206 "add $8, %rax\n" /* search for auxv using rax, it follows the */ 207 "cmp -8(%rax), %rbp\n" /* ... NULL after last env (rbp is zero here) */ 208 "jnz 0b\n" 209 "mov %rax, _auxv\n" /* save it into _auxv */ 210 "and $-16, %rsp\n" /* x86 ABI : esp must be 16-byte aligned before call */ 211 "call main\n" /* main() returns the status code, we'll exit with it. */ 212 "mov %eax, %edi\n" /* retrieve exit code (32 bit) */ 213 "mov $60, %eax\n" /* NR_exit == 60 */ 214 "syscall\n" /* really exit */ 215 "hlt\n" /* ensure it does not return */ 216 ); 217 __builtin_unreachable(); 218 } 219 220 #endif /* _NOLIBC_ARCH_X86_64_H */ 221