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 */
_start(void)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