arch-i386.h 10 KB

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