1 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */ 2 /* 3 * AARCH64 specific definitions for NOLIBC 4 * Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu> 5 */ 6 7 #ifndef _NOLIBC_ARCH_AARCH64_H 8 #define _NOLIBC_ARCH_AARCH64_H 9 10 /* The struct returned by the newfstatat() syscall. Differs slightly from the 11 * x86_64's stat one by field ordering, so be careful. 12 */ 13 struct sys_stat_struct { 14 unsigned long st_dev; 15 unsigned long st_ino; 16 unsigned int st_mode; 17 unsigned int st_nlink; 18 unsigned int st_uid; 19 unsigned int st_gid; 20 21 unsigned long st_rdev; 22 unsigned long __pad1; 23 long st_size; 24 int st_blksize; 25 int __pad2; 26 27 long st_blocks; 28 long st_atime; 29 unsigned long st_atime_nsec; 30 long st_mtime; 31 32 unsigned long st_mtime_nsec; 33 long st_ctime; 34 unsigned long st_ctime_nsec; 35 unsigned int __unused[2]; 36 }; 37 38 /* Syscalls for AARCH64 : 39 * - registers are 64-bit 40 * - stack is 16-byte aligned 41 * - syscall number is passed in x8 42 * - arguments are in x0, x1, x2, x3, x4, x5 43 * - the system call is performed by calling svc 0 44 * - syscall return comes in x0. 45 * - the arguments are cast to long and assigned into the target registers 46 * which are then simply passed as registers to the asm code, so that we 47 * don't have to experience issues with register constraints. 48 * 49 * On aarch64, select() is not implemented so we have to use pselect6(). 50 */ 51 #define __ARCH_WANT_SYS_PSELECT6 52 53 #define my_syscall0(num) \ 54 ({ \ 55 register long _num __asm__ ("x8") = (num); \ 56 register long _arg1 __asm__ ("x0"); \ 57 \ 58 __asm__ volatile ( \ 59 "svc #0\n" \ 60 : "=r"(_arg1) \ 61 : "r"(_num) \ 62 : "memory", "cc" \ 63 ); \ 64 _arg1; \ 65 }) 66 67 #define my_syscall1(num, arg1) \ 68 ({ \ 69 register long _num __asm__ ("x8") = (num); \ 70 register long _arg1 __asm__ ("x0") = (long)(arg1); \ 71 \ 72 __asm__ volatile ( \ 73 "svc #0\n" \ 74 : "=r"(_arg1) \ 75 : "r"(_arg1), \ 76 "r"(_num) \ 77 : "memory", "cc" \ 78 ); \ 79 _arg1; \ 80 }) 81 82 #define my_syscall2(num, arg1, arg2) \ 83 ({ \ 84 register long _num __asm__ ("x8") = (num); \ 85 register long _arg1 __asm__ ("x0") = (long)(arg1); \ 86 register long _arg2 __asm__ ("x1") = (long)(arg2); \ 87 \ 88 __asm__ volatile ( \ 89 "svc #0\n" \ 90 : "=r"(_arg1) \ 91 : "r"(_arg1), "r"(_arg2), \ 92 "r"(_num) \ 93 : "memory", "cc" \ 94 ); \ 95 _arg1; \ 96 }) 97 98 #define my_syscall3(num, arg1, arg2, arg3) \ 99 ({ \ 100 register long _num __asm__ ("x8") = (num); \ 101 register long _arg1 __asm__ ("x0") = (long)(arg1); \ 102 register long _arg2 __asm__ ("x1") = (long)(arg2); \ 103 register long _arg3 __asm__ ("x2") = (long)(arg3); \ 104 \ 105 __asm__ volatile ( \ 106 "svc #0\n" \ 107 : "=r"(_arg1) \ 108 : "r"(_arg1), "r"(_arg2), "r"(_arg3), \ 109 "r"(_num) \ 110 : "memory", "cc" \ 111 ); \ 112 _arg1; \ 113 }) 114 115 #define my_syscall4(num, arg1, arg2, arg3, arg4) \ 116 ({ \ 117 register long _num __asm__ ("x8") = (num); \ 118 register long _arg1 __asm__ ("x0") = (long)(arg1); \ 119 register long _arg2 __asm__ ("x1") = (long)(arg2); \ 120 register long _arg3 __asm__ ("x2") = (long)(arg3); \ 121 register long _arg4 __asm__ ("x3") = (long)(arg4); \ 122 \ 123 __asm__ volatile ( \ 124 "svc #0\n" \ 125 : "=r"(_arg1) \ 126 : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \ 127 "r"(_num) \ 128 : "memory", "cc" \ 129 ); \ 130 _arg1; \ 131 }) 132 133 #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \ 134 ({ \ 135 register long _num __asm__ ("x8") = (num); \ 136 register long _arg1 __asm__ ("x0") = (long)(arg1); \ 137 register long _arg2 __asm__ ("x1") = (long)(arg2); \ 138 register long _arg3 __asm__ ("x2") = (long)(arg3); \ 139 register long _arg4 __asm__ ("x3") = (long)(arg4); \ 140 register long _arg5 __asm__ ("x4") = (long)(arg5); \ 141 \ 142 __asm__ volatile ( \ 143 "svc #0\n" \ 144 : "=r" (_arg1) \ 145 : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ 146 "r"(_num) \ 147 : "memory", "cc" \ 148 ); \ 149 _arg1; \ 150 }) 151 152 #define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \ 153 ({ \ 154 register long _num __asm__ ("x8") = (num); \ 155 register long _arg1 __asm__ ("x0") = (long)(arg1); \ 156 register long _arg2 __asm__ ("x1") = (long)(arg2); \ 157 register long _arg3 __asm__ ("x2") = (long)(arg3); \ 158 register long _arg4 __asm__ ("x3") = (long)(arg4); \ 159 register long _arg5 __asm__ ("x4") = (long)(arg5); \ 160 register long _arg6 __asm__ ("x5") = (long)(arg6); \ 161 \ 162 __asm__ volatile ( \ 163 "svc #0\n" \ 164 : "=r" (_arg1) \ 165 : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ 166 "r"(_arg6), "r"(_num) \ 167 : "memory", "cc" \ 168 ); \ 169 _arg1; \ 170 }) 171 172 char **environ __attribute__((weak)); 173 const unsigned long *_auxv __attribute__((weak)); 174 175 /* startup code */ 176 void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) _start(void) 177 { 178 __asm__ volatile ( 179 "ldr x0, [sp]\n" // argc (x0) was in the stack 180 "add x1, sp, 8\n" // argv (x1) = sp 181 "lsl x2, x0, 3\n" // envp (x2) = 8*argc ... 182 "add x2, x2, 8\n" // + 8 (skip null) 183 "add x2, x2, x1\n" // + argv 184 "adrp x3, environ\n" // x3 = &environ (high bits) 185 "str x2, [x3, #:lo12:environ]\n" // store envp into environ 186 "mov x4, x2\n" // search for auxv (follows NULL after last env) 187 "0:\n" 188 "ldr x5, [x4], 8\n" // x5 = *x4; x4 += 8 189 "cbnz x5, 0b\n" // and stop at NULL after last env 190 "adrp x3, _auxv\n" // x3 = &_auxv (high bits) 191 "str x4, [x3, #:lo12:_auxv]\n" // store x4 into _auxv 192 "and sp, x1, -16\n" // sp must be 16-byte aligned in the callee 193 "bl main\n" // main() returns the status code, we'll exit with it. 194 "mov x8, 93\n" // NR_exit == 93 195 "svc #0\n" 196 ); 197 __builtin_unreachable(); 198 } 199 #endif // _NOLIBC_ARCH_AARCH64_H 200