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