xref: /linux/tools/include/nolibc/arch-x86.h (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */
2 /*
3  * x86 specific definitions for NOLIBC (both 32- and 64-bit)
4  * Copyright (C) 2017-2025 Willy Tarreau <w@1wt.eu>
5  */
6 
7 #ifndef _NOLIBC_ARCH_X86_H
8 #define _NOLIBC_ARCH_X86_H
9 
10 #include "compiler.h"
11 #include "crt.h"
12 
13 #if !defined(__x86_64__)
14 
15 /* Syscalls for i386 :
16  *   - mostly similar to x86_64
17  *   - registers are 32-bit
18  *   - syscall number is passed in eax
19  *   - arguments are in ebx, ecx, edx, esi, edi, ebp respectively
20  *   - all registers are preserved (except eax of course)
21  *   - the system call is performed by calling int $0x80
22  *   - syscall return comes in eax
23  *   - the arguments are cast to long and assigned into the target registers
24  *     which are then simply passed as registers to the asm code, so that we
25  *     don't have to experience issues with register constraints.
26  *   - the syscall number is always specified last in order to allow to force
27  *     some registers before (gcc refuses a %-register at the last position).
28  */
29 
30 #define __nolibc_syscall0(num)                                                \
31 ({                                                                            \
32 	long _ret;                                                            \
33 	register long _num __asm__ ("eax") = (num);                           \
34 									      \
35 	__asm__ volatile (                                                    \
36 		"int $0x80\n"                                                 \
37 		: "=a" (_ret)                                                 \
38 		: "0"(_num)                                                   \
39 		: "memory", "cc"                                              \
40 	);                                                                    \
41 	_ret;                                                                 \
42 })
43 
44 #define __nolibc_syscall1(num, arg1)                                          \
45 ({                                                                            \
46 	long _ret;                                                            \
47 	register long _num __asm__ ("eax") = (num);                           \
48 	register long _arg1 __asm__ ("ebx") = (long)(arg1);                   \
49 									      \
50 	__asm__ volatile (                                                    \
51 		"int $0x80\n"                                                 \
52 		: "=a" (_ret)                                                 \
53 		: "r"(_arg1),                                                 \
54 		  "0"(_num)                                                   \
55 		: "memory", "cc"                                              \
56 	);                                                                    \
57 	_ret;                                                                 \
58 })
59 
60 #define __nolibc_syscall2(num, arg1, arg2)                                    \
61 ({                                                                            \
62 	long _ret;                                                            \
63 	register long _num __asm__ ("eax") = (num);                           \
64 	register long _arg1 __asm__ ("ebx") = (long)(arg1);                   \
65 	register long _arg2 __asm__ ("ecx") = (long)(arg2);                   \
66 									      \
67 	__asm__ volatile (                                                    \
68 		"int $0x80\n"                                                 \
69 		: "=a" (_ret)                                                 \
70 		: "r"(_arg1), "r"(_arg2),                                     \
71 		  "0"(_num)                                                   \
72 		: "memory", "cc"                                              \
73 	);                                                                    \
74 	_ret;                                                                 \
75 })
76 
77 #define __nolibc_syscall3(num, arg1, arg2, arg3)                              \
78 ({                                                                            \
79 	long _ret;                                                            \
80 	register long _num __asm__ ("eax") = (num);                           \
81 	register long _arg1 __asm__ ("ebx") = (long)(arg1);                   \
82 	register long _arg2 __asm__ ("ecx") = (long)(arg2);                   \
83 	register long _arg3 __asm__ ("edx") = (long)(arg3);                   \
84 									      \
85 	__asm__ volatile (                                                    \
86 		"int $0x80\n"                                                 \
87 		: "=a" (_ret)                                                 \
88 		: "r"(_arg1), "r"(_arg2), "r"(_arg3),                         \
89 		  "0"(_num)                                                   \
90 		: "memory", "cc"                                              \
91 	);                                                                    \
92 	_ret;                                                                 \
93 })
94 
95 #define __nolibc_syscall4(num, arg1, arg2, arg3, arg4)                        \
96 ({                                                                            \
97 	long _ret;                                                            \
98 	register long _num __asm__ ("eax") = (num);                           \
99 	register long _arg1 __asm__ ("ebx") = (long)(arg1);                   \
100 	register long _arg2 __asm__ ("ecx") = (long)(arg2);                   \
101 	register long _arg3 __asm__ ("edx") = (long)(arg3);                   \
102 	register long _arg4 __asm__ ("esi") = (long)(arg4);                   \
103 									      \
104 	__asm__ volatile (                                                    \
105 		"int $0x80\n"                                                 \
106 		: "=a" (_ret)                                                 \
107 		: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4),             \
108 		  "0"(_num)                                                   \
109 		: "memory", "cc"                                              \
110 	);                                                                    \
111 	_ret;                                                                 \
112 })
113 
114 #define __nolibc_syscall5(num, arg1, arg2, arg3, arg4, arg5)                  \
115 ({                                                                            \
116 	long _ret;                                                            \
117 	register long _num __asm__ ("eax") = (num);                           \
118 	register long _arg1 __asm__ ("ebx") = (long)(arg1);                   \
119 	register long _arg2 __asm__ ("ecx") = (long)(arg2);                   \
120 	register long _arg3 __asm__ ("edx") = (long)(arg3);                   \
121 	register long _arg4 __asm__ ("esi") = (long)(arg4);                   \
122 	register long _arg5 __asm__ ("edi") = (long)(arg5);                   \
123 									      \
124 	__asm__ volatile (                                                    \
125 		"int $0x80\n"                                                 \
126 		: "=a" (_ret)                                                 \
127 		: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
128 		  "0"(_num)                                                   \
129 		: "memory", "cc"                                              \
130 	);                                                                    \
131 	_ret;                                                                 \
132 })
133 
134 #define __nolibc_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6)	      \
135 ({								              \
136 	long _eax  = (long)(num);				              \
137 	long _arg6 = (long)(arg6); /* Always in memory */	              \
138 	__asm__ volatile (					              \
139 		"pushl	%[_arg6]\n\t"				              \
140 		"pushl	%%ebp\n\t"				              \
141 		"movl	4(%%esp),%%ebp\n\t"			              \
142 		"int	$0x80\n\t"				              \
143 		"popl	%%ebp\n\t"				              \
144 		"addl	$4,%%esp\n\t"				              \
145 		: "+a"(_eax)		/* %eax */		              \
146 		: "b"(arg1),		/* %ebx */		              \
147 		  "c"(arg2),		/* %ecx */		              \
148 		  "d"(arg3),		/* %edx */		              \
149 		  "S"(arg4),		/* %esi */		              \
150 		  "D"(arg5),		/* %edi */		              \
151 		  [_arg6]"m"(_arg6)	/* memory */		              \
152 		: "memory", "cc"				              \
153 	);							              \
154 	_eax;							              \
155 })
156 
157 #ifndef NOLIBC_NO_RUNTIME
158 /* startup code */
159 /*
160  * i386 System V ABI mandates:
161  * 1) last pushed argument must be 16-byte aligned.
162  * 2) The deepest stack frame should be set to zero
163  *
164  */
165 void __attribute__((weak, noreturn)) __nolibc_entrypoint __nolibc_no_stack_protector _start(void)
166 {
167 	__asm__ volatile (
168 		"xor  %ebp, %ebp\n"       /* zero the stack frame                                */
169 		"mov  %esp, %eax\n"       /* save stack pointer to %eax, as arg1 of _start_c     */
170 		"sub  $12, %esp\n"        /* sub 12 to keep it aligned after the push %eax       */
171 		"push %eax\n"             /* push arg1 on stack to support plain stack modes too */
172 		"call _start_c\n"         /* transfer to c runtime                               */
173 		"hlt\n"                   /* ensure it does not return                           */
174 	);
175 	__nolibc_entrypoint_epilogue();
176 }
177 #endif /* NOLIBC_NO_RUNTIME */
178 
179 #else /* !defined(__x86_64__) */
180 
181 /* Syscalls for x86_64 :
182  *   - registers are 64-bit
183  *   - syscall number is passed in rax
184  *   - arguments are in rdi, rsi, rdx, r10, r8, r9 respectively
185  *   - the system call is performed by calling the syscall instruction
186  *   - syscall return comes in rax
187  *   - rcx and r11 are clobbered, others are preserved.
188  *   - the arguments are cast to long and assigned into the target registers
189  *     which are then simply passed as registers to the asm code, so that we
190  *     don't have to experience issues with register constraints.
191  *   - the syscall number is always specified last in order to allow to force
192  *     some registers before (gcc refuses a %-register at the last position).
193  *   - see also x86-64 ABI section A.2 AMD64 Linux Kernel Conventions, A.2.1
194  *     Calling Conventions.
195  *
196  * Link x86-64 ABI: https://gitlab.com/x86-psABIs/x86-64-ABI/-/wikis/home
197  *
198  */
199 
200 #define __nolibc_syscall0(num)                                                \
201 ({                                                                            \
202 	long long _ret;                                                       \
203 	register long long _num  __asm__ ("rax") = (num);                     \
204 									      \
205 	__asm__ volatile (                                                    \
206 		"syscall\n"                                                   \
207 		: "=a"(_ret)                                                  \
208 		: "0"(_num)                                                   \
209 		: "rcx", "r11", "memory", "cc"                                \
210 	);                                                                    \
211 	_ret;                                                                 \
212 })
213 
214 #define __nolibc_syscall1(num, arg1)                                          \
215 ({                                                                            \
216 	long long _ret;                                                       \
217 	register long long _num  __asm__ ("rax") = (num);                     \
218 	register long long _arg1 __asm__ ("rdi") = __nolibc_arg_to_reg(arg1); \
219 									      \
220 	__asm__ volatile (                                                    \
221 		"syscall\n"                                                   \
222 		: "=a"(_ret)                                                  \
223 		: "r"(_arg1),                                                 \
224 		  "0"(_num)                                                   \
225 		: "rcx", "r11", "memory", "cc"                                \
226 	);                                                                    \
227 	_ret;                                                                 \
228 })
229 
230 #define __nolibc_syscall2(num, arg1, arg2)                                    \
231 ({                                                                            \
232 	long long _ret;                                                       \
233 	register long long _num  __asm__ ("rax") = (num);                     \
234 	register long long _arg1 __asm__ ("rdi") = __nolibc_arg_to_reg(arg1); \
235 	register long long _arg2 __asm__ ("rsi") = __nolibc_arg_to_reg(arg2); \
236 									      \
237 	__asm__ volatile (                                                    \
238 		"syscall\n"                                                   \
239 		: "=a"(_ret)                                                  \
240 		: "r"(_arg1), "r"(_arg2),                                     \
241 		  "0"(_num)                                                   \
242 		: "rcx", "r11", "memory", "cc"                                \
243 	);                                                                    \
244 	_ret;                                                                 \
245 })
246 
247 #define __nolibc_syscall3(num, arg1, arg2, arg3)                              \
248 ({                                                                            \
249 	long long _ret;                                                       \
250 	register long long _num  __asm__ ("rax") = (num);                     \
251 	register long long _arg1 __asm__ ("rdi") = __nolibc_arg_to_reg(arg1); \
252 	register long long _arg2 __asm__ ("rsi") = __nolibc_arg_to_reg(arg2); \
253 	register long long _arg3 __asm__ ("rdx") = __nolibc_arg_to_reg(arg3); \
254 									      \
255 	__asm__ volatile (                                                    \
256 		"syscall\n"                                                   \
257 		: "=a"(_ret)                                                  \
258 		: "r"(_arg1), "r"(_arg2), "r"(_arg3),                         \
259 		  "0"(_num)                                                   \
260 		: "rcx", "r11", "memory", "cc"                                \
261 	);                                                                    \
262 	_ret;                                                                 \
263 })
264 
265 #define __nolibc_syscall4(num, arg1, arg2, arg3, arg4)                        \
266 ({                                                                            \
267 	long long _ret;                                                       \
268 	register long long _num  __asm__ ("rax") = (num);                     \
269 	register long long _arg1 __asm__ ("rdi") = __nolibc_arg_to_reg(arg1); \
270 	register long long _arg2 __asm__ ("rsi") = __nolibc_arg_to_reg(arg2); \
271 	register long long _arg3 __asm__ ("rdx") = __nolibc_arg_to_reg(arg3); \
272 	register long long _arg4 __asm__ ("r10") = __nolibc_arg_to_reg(arg4); \
273 									      \
274 	__asm__ volatile (                                                    \
275 		"syscall\n"                                                   \
276 		: "=a"(_ret)                                                  \
277 		: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4),             \
278 		  "0"(_num)                                                   \
279 		: "rcx", "r11", "memory", "cc"                                \
280 	);                                                                    \
281 	_ret;                                                                 \
282 })
283 
284 #define __nolibc_syscall5(num, arg1, arg2, arg3, arg4, arg5)                  \
285 ({                                                                            \
286 	long long _ret;                                                       \
287 	register long long _num  __asm__ ("rax") = (num);                     \
288 	register long long _arg1 __asm__ ("rdi") = __nolibc_arg_to_reg(arg1); \
289 	register long long _arg2 __asm__ ("rsi") = __nolibc_arg_to_reg(arg2); \
290 	register long long _arg3 __asm__ ("rdx") = __nolibc_arg_to_reg(arg3); \
291 	register long long _arg4 __asm__ ("r10") = __nolibc_arg_to_reg(arg4); \
292 	register long long _arg5 __asm__ ("r8")  = __nolibc_arg_to_reg(arg5); \
293 									      \
294 	__asm__ volatile (                                                    \
295 		"syscall\n"                                                   \
296 		: "=a"(_ret)                                                  \
297 		: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
298 		  "0"(_num)                                                   \
299 		: "rcx", "r11", "memory", "cc"                                \
300 	);                                                                    \
301 	_ret;                                                                 \
302 })
303 
304 #define __nolibc_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6)            \
305 ({                                                                            \
306 	long long _ret;                                                       \
307 	register long long _num  __asm__ ("rax") = (num);                     \
308 	register long long _arg1 __asm__ ("rdi") = __nolibc_arg_to_reg(arg1); \
309 	register long long _arg2 __asm__ ("rsi") = __nolibc_arg_to_reg(arg2); \
310 	register long long _arg3 __asm__ ("rdx") = __nolibc_arg_to_reg(arg3); \
311 	register long long _arg4 __asm__ ("r10") = __nolibc_arg_to_reg(arg4); \
312 	register long long _arg5 __asm__ ("r8")  = __nolibc_arg_to_reg(arg5); \
313 	register long long _arg6 __asm__ ("r9")  = __nolibc_arg_to_reg(arg6); \
314 									      \
315 	__asm__ volatile (                                                    \
316 		"syscall\n"                                                   \
317 		: "=a"(_ret)                                                  \
318 		: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
319 		  "r"(_arg6), "0"(_num)                                       \
320 		: "rcx", "r11", "memory", "cc"                                \
321 	);                                                                    \
322 	_ret;                                                                 \
323 })
324 
325 #ifndef NOLIBC_NO_RUNTIME
326 /* startup code */
327 /*
328  * x86-64 System V ABI mandates:
329  * 1) %rsp must be 16-byte aligned right before the function call.
330  * 2) The deepest stack frame should be zero (the %rbp).
331  *
332  */
333 void __attribute__((weak, noreturn)) __nolibc_entrypoint __nolibc_no_stack_protector _start(void)
334 {
335 	__asm__ volatile (
336 		"xor  %ebp, %ebp\n"       /* zero the stack frame                            */
337 		"mov  %rsp, %rdi\n"       /* save stack pointer to %rdi, as arg1 of _start_c */
338 		"call _start_c\n"         /* transfer to c runtime                           */
339 		"hlt\n"                   /* ensure it does not return                       */
340 	);
341 	__nolibc_entrypoint_epilogue();
342 }
343 #endif /* NOLIBC_NO_RUNTIME */
344 
345 #define NOLIBC_ARCH_HAS_MEMMOVE
346 void *memmove(void *dst, const void *src, size_t len);
347 
348 #define NOLIBC_ARCH_HAS_MEMCPY
349 void *memcpy(void *dst, const void *src, size_t len);
350 
351 #define NOLIBC_ARCH_HAS_MEMSET
352 void *memset(void *dst, int c, size_t len);
353 
354 __asm__ (
355 ".pushsection .text.nolibc_memmove_memcpy\n"
356 ".weak memmove\n"
357 ".weak memcpy\n"
358 "memmove:\n"
359 "memcpy:\n"
360 	"movq %rdx, %rcx\n\t"
361 	"movq %rdi, %rax\n\t"
362 	"movq %rdi, %rdx\n\t"
363 	"subq %rsi, %rdx\n\t"
364 	"cmpq %rcx, %rdx\n\t"
365 	"jb   1f\n\t"
366 	"rep movsb\n\t"
367 	"retq\n"
368 "1:" /* backward copy */
369 	"leaq -1(%rdi, %rcx, 1), %rdi\n\t"
370 	"leaq -1(%rsi, %rcx, 1), %rsi\n\t"
371 	"std\n\t"
372 	"rep movsb\n\t"
373 	"cld\n\t"
374 	"retq\n"
375 ".popsection\n"
376 
377 ".pushsection .text.nolibc_memset\n"
378 ".weak memset\n"
379 "memset:\n"
380 	"xchgl %eax, %esi\n\t"
381 	"movq  %rdx, %rcx\n\t"
382 	"pushq %rdi\n\t"
383 	"rep stosb\n\t"
384 	"popq  %rax\n\t"
385 	"retq\n"
386 ".popsection\n"
387 );
388 
389 #endif /* !defined(__x86_64__) */
390 #endif /* _NOLIBC_ARCH_X86_H */
391