Loading...
1/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
2/*
3 * i386 specific definitions for NOLIBC
4 * Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu>
5 */
6
7#ifndef _NOLIBC_ARCH_I386_H
8#define _NOLIBC_ARCH_I386_H
9
10#include "compiler.h"
11#include "crt.h"
12
13/* Syscalls for i386 :
14 * - mostly similar to x86_64
15 * - registers are 32-bit
16 * - syscall number is passed in eax
17 * - arguments are in ebx, ecx, edx, esi, edi, ebp respectively
18 * - all registers are preserved (except eax of course)
19 * - the system call is performed by calling int $0x80
20 * - syscall return comes in eax
21 * - the arguments are cast to long and assigned into the target registers
22 * which are then simply passed as registers to the asm code, so that we
23 * don't have to experience issues with register constraints.
24 * - the syscall number is always specified last in order to allow to force
25 * some registers before (gcc refuses a %-register at the last position).
26 *
27 * Also, i386 supports the old_select syscall if newselect is not available
28 */
29#define __ARCH_WANT_SYS_OLD_SELECT
30
31#define my_syscall0(num) \
32({ \
33 long _ret; \
34 register long _num __asm__ ("eax") = (num); \
35 \
36 __asm__ volatile ( \
37 "int $0x80\n" \
38 : "=a" (_ret) \
39 : "0"(_num) \
40 : "memory", "cc" \
41 ); \
42 _ret; \
43})
44
45#define my_syscall1(num, arg1) \
46({ \
47 long _ret; \
48 register long _num __asm__ ("eax") = (num); \
49 register long _arg1 __asm__ ("ebx") = (long)(arg1); \
50 \
51 __asm__ volatile ( \
52 "int $0x80\n" \
53 : "=a" (_ret) \
54 : "r"(_arg1), \
55 "0"(_num) \
56 : "memory", "cc" \
57 ); \
58 _ret; \
59})
60
61#define my_syscall2(num, arg1, arg2) \
62({ \
63 long _ret; \
64 register long _num __asm__ ("eax") = (num); \
65 register long _arg1 __asm__ ("ebx") = (long)(arg1); \
66 register long _arg2 __asm__ ("ecx") = (long)(arg2); \
67 \
68 __asm__ volatile ( \
69 "int $0x80\n" \
70 : "=a" (_ret) \
71 : "r"(_arg1), "r"(_arg2), \
72 "0"(_num) \
73 : "memory", "cc" \
74 ); \
75 _ret; \
76})
77
78#define my_syscall3(num, arg1, arg2, arg3) \
79({ \
80 long _ret; \
81 register long _num __asm__ ("eax") = (num); \
82 register long _arg1 __asm__ ("ebx") = (long)(arg1); \
83 register long _arg2 __asm__ ("ecx") = (long)(arg2); \
84 register long _arg3 __asm__ ("edx") = (long)(arg3); \
85 \
86 __asm__ volatile ( \
87 "int $0x80\n" \
88 : "=a" (_ret) \
89 : "r"(_arg1), "r"(_arg2), "r"(_arg3), \
90 "0"(_num) \
91 : "memory", "cc" \
92 ); \
93 _ret; \
94})
95
96#define my_syscall4(num, arg1, arg2, arg3, arg4) \
97({ \
98 long _ret; \
99 register long _num __asm__ ("eax") = (num); \
100 register long _arg1 __asm__ ("ebx") = (long)(arg1); \
101 register long _arg2 __asm__ ("ecx") = (long)(arg2); \
102 register long _arg3 __asm__ ("edx") = (long)(arg3); \
103 register long _arg4 __asm__ ("esi") = (long)(arg4); \
104 \
105 __asm__ volatile ( \
106 "int $0x80\n" \
107 : "=a" (_ret) \
108 : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \
109 "0"(_num) \
110 : "memory", "cc" \
111 ); \
112 _ret; \
113})
114
115#define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \
116({ \
117 long _ret; \
118 register long _num __asm__ ("eax") = (num); \
119 register long _arg1 __asm__ ("ebx") = (long)(arg1); \
120 register long _arg2 __asm__ ("ecx") = (long)(arg2); \
121 register long _arg3 __asm__ ("edx") = (long)(arg3); \
122 register long _arg4 __asm__ ("esi") = (long)(arg4); \
123 register long _arg5 __asm__ ("edi") = (long)(arg5); \
124 \
125 __asm__ volatile ( \
126 "int $0x80\n" \
127 : "=a" (_ret) \
128 : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
129 "0"(_num) \
130 : "memory", "cc" \
131 ); \
132 _ret; \
133})
134
135#define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \
136({ \
137 long _eax = (long)(num); \
138 long _arg6 = (long)(arg6); /* Always in memory */ \
139 __asm__ volatile ( \
140 "pushl %[_arg6]\n\t" \
141 "pushl %%ebp\n\t" \
142 "movl 4(%%esp),%%ebp\n\t" \
143 "int $0x80\n\t" \
144 "popl %%ebp\n\t" \
145 "addl $4,%%esp\n\t" \
146 : "+a"(_eax) /* %eax */ \
147 : "b"(arg1), /* %ebx */ \
148 "c"(arg2), /* %ecx */ \
149 "d"(arg3), /* %edx */ \
150 "S"(arg4), /* %esi */ \
151 "D"(arg5), /* %edi */ \
152 [_arg6]"m"(_arg6) /* memory */ \
153 : "memory", "cc" \
154 ); \
155 _eax; \
156})
157
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 */
165void __attribute__((weak, noreturn, optimize("Os", "omit-frame-pointer"))) __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 "add $12, %esp\n" /* avoid over-estimating after the 'and' & 'sub' below */
171 "and $-16, %esp\n" /* the %esp must be 16-byte aligned on 'call' */
172 "sub $12, %esp\n" /* sub 12 to keep it aligned after the push %eax */
173 "push %eax\n" /* push arg1 on stack to support plain stack modes too */
174 "call _start_c\n" /* transfer to c runtime */
175 "hlt\n" /* ensure it does not return */
176 );
177 __builtin_unreachable();
178}
179
180#endif /* _NOLIBC_ARCH_I386_H */
1/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
2/*
3 * i386 specific definitions for NOLIBC
4 * Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu>
5 */
6
7#ifndef _NOLIBC_ARCH_I386_H
8#define _NOLIBC_ARCH_I386_H
9
10/* O_* macros for fcntl/open are architecture-specific */
11#define O_RDONLY 0
12#define O_WRONLY 1
13#define O_RDWR 2
14#define O_CREAT 0x40
15#define O_EXCL 0x80
16#define O_NOCTTY 0x100
17#define O_TRUNC 0x200
18#define O_APPEND 0x400
19#define O_NONBLOCK 0x800
20#define O_DIRECTORY 0x10000
21
22/* The struct returned by the stat() syscall, 32-bit only, the syscall returns
23 * exactly 56 bytes (stops before the unused array).
24 */
25struct sys_stat_struct {
26 unsigned long st_dev;
27 unsigned long st_ino;
28 unsigned short st_mode;
29 unsigned short st_nlink;
30 unsigned short st_uid;
31 unsigned short st_gid;
32
33 unsigned long st_rdev;
34 unsigned long st_size;
35 unsigned long st_blksize;
36 unsigned long st_blocks;
37
38 unsigned long st_atime;
39 unsigned long st_atime_nsec;
40 unsigned long st_mtime;
41 unsigned long st_mtime_nsec;
42
43 unsigned long st_ctime;
44 unsigned long st_ctime_nsec;
45 unsigned long __unused[2];
46};
47
48/* Syscalls for i386 :
49 * - mostly similar to x86_64
50 * - registers are 32-bit
51 * - syscall number is passed in eax
52 * - arguments are in ebx, ecx, edx, esi, edi, ebp respectively
53 * - all registers are preserved (except eax of course)
54 * - the system call is performed by calling int $0x80
55 * - syscall return comes in eax
56 * - the arguments are cast to long and assigned into the target registers
57 * which are then simply passed as registers to the asm code, so that we
58 * don't have to experience issues with register constraints.
59 * - the syscall number is always specified last in order to allow to force
60 * some registers before (gcc refuses a %-register at the last position).
61 *
62 * Also, i386 supports the old_select syscall if newselect is not available
63 */
64#define __ARCH_WANT_SYS_OLD_SELECT
65
66#define my_syscall0(num) \
67({ \
68 long _ret; \
69 register long _num __asm__ ("eax") = (num); \
70 \
71 __asm__ volatile ( \
72 "int $0x80\n" \
73 : "=a" (_ret) \
74 : "0"(_num) \
75 : "memory", "cc" \
76 ); \
77 _ret; \
78})
79
80#define my_syscall1(num, arg1) \
81({ \
82 long _ret; \
83 register long _num __asm__ ("eax") = (num); \
84 register long _arg1 __asm__ ("ebx") = (long)(arg1); \
85 \
86 __asm__ volatile ( \
87 "int $0x80\n" \
88 : "=a" (_ret) \
89 : "r"(_arg1), \
90 "0"(_num) \
91 : "memory", "cc" \
92 ); \
93 _ret; \
94})
95
96#define my_syscall2(num, arg1, arg2) \
97({ \
98 long _ret; \
99 register long _num __asm__ ("eax") = (num); \
100 register long _arg1 __asm__ ("ebx") = (long)(arg1); \
101 register long _arg2 __asm__ ("ecx") = (long)(arg2); \
102 \
103 __asm__ volatile ( \
104 "int $0x80\n" \
105 : "=a" (_ret) \
106 : "r"(_arg1), "r"(_arg2), \
107 "0"(_num) \
108 : "memory", "cc" \
109 ); \
110 _ret; \
111})
112
113#define my_syscall3(num, arg1, arg2, arg3) \
114({ \
115 long _ret; \
116 register long _num __asm__ ("eax") = (num); \
117 register long _arg1 __asm__ ("ebx") = (long)(arg1); \
118 register long _arg2 __asm__ ("ecx") = (long)(arg2); \
119 register long _arg3 __asm__ ("edx") = (long)(arg3); \
120 \
121 __asm__ volatile ( \
122 "int $0x80\n" \
123 : "=a" (_ret) \
124 : "r"(_arg1), "r"(_arg2), "r"(_arg3), \
125 "0"(_num) \
126 : "memory", "cc" \
127 ); \
128 _ret; \
129})
130
131#define my_syscall4(num, arg1, arg2, arg3, arg4) \
132({ \
133 long _ret; \
134 register long _num __asm__ ("eax") = (num); \
135 register long _arg1 __asm__ ("ebx") = (long)(arg1); \
136 register long _arg2 __asm__ ("ecx") = (long)(arg2); \
137 register long _arg3 __asm__ ("edx") = (long)(arg3); \
138 register long _arg4 __asm__ ("esi") = (long)(arg4); \
139 \
140 __asm__ volatile ( \
141 "int $0x80\n" \
142 : "=a" (_ret) \
143 : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \
144 "0"(_num) \
145 : "memory", "cc" \
146 ); \
147 _ret; \
148})
149
150#define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \
151({ \
152 long _ret; \
153 register long _num __asm__ ("eax") = (num); \
154 register long _arg1 __asm__ ("ebx") = (long)(arg1); \
155 register long _arg2 __asm__ ("ecx") = (long)(arg2); \
156 register long _arg3 __asm__ ("edx") = (long)(arg3); \
157 register long _arg4 __asm__ ("esi") = (long)(arg4); \
158 register long _arg5 __asm__ ("edi") = (long)(arg5); \
159 \
160 __asm__ volatile ( \
161 "int $0x80\n" \
162 : "=a" (_ret) \
163 : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
164 "0"(_num) \
165 : "memory", "cc" \
166 ); \
167 _ret; \
168})
169
170#define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \
171({ \
172 long _eax = (long)(num); \
173 long _arg6 = (long)(arg6); /* Always in memory */ \
174 __asm__ volatile ( \
175 "pushl %[_arg6]\n\t" \
176 "pushl %%ebp\n\t" \
177 "movl 4(%%esp),%%ebp\n\t" \
178 "int $0x80\n\t" \
179 "popl %%ebp\n\t" \
180 "addl $4,%%esp\n\t" \
181 : "+a"(_eax) /* %eax */ \
182 : "b"(arg1), /* %ebx */ \
183 "c"(arg2), /* %ecx */ \
184 "d"(arg3), /* %edx */ \
185 "S"(arg4), /* %esi */ \
186 "D"(arg5), /* %edi */ \
187 [_arg6]"m"(_arg6) /* memory */ \
188 : "memory", "cc" \
189 ); \
190 _eax; \
191})
192
193/* startup code */
194/*
195 * i386 System V ABI mandates:
196 * 1) last pushed argument must be 16-byte aligned.
197 * 2) The deepest stack frame should be set to zero
198 *
199 */
200__asm__ (".section .text\n"
201 ".weak _start\n"
202 "_start:\n"
203 "pop %eax\n" // argc (first arg, %eax)
204 "mov %esp, %ebx\n" // argv[] (second arg, %ebx)
205 "lea 4(%ebx,%eax,4),%ecx\n" // then a NULL then envp (third arg, %ecx)
206 "xor %ebp, %ebp\n" // zero the stack frame
207 "and $-16, %esp\n" // x86 ABI : esp must be 16-byte aligned before
208 "sub $4, %esp\n" // the call instruction (args are aligned)
209 "push %ecx\n" // push all registers on the stack so that we
210 "push %ebx\n" // support both regparm and plain stack modes
211 "push %eax\n"
212 "call main\n" // main() returns the status code in %eax
213 "mov %eax, %ebx\n" // retrieve exit code (32-bit int)
214 "movl $1, %eax\n" // NR_exit == 1
215 "int $0x80\n" // exit now
216 "hlt\n" // ensure it does not
217 "");
218
219#endif // _NOLIBC_ARCH_I386_H