1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ ~~ System calls for the Linux kernel ~~
~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~
~ The kernel preserves every register except rax, rcx, and r11. The system
~ call number goes in rax, as does the return value. Parameters go in rdi,
~ rsi, rdx, r10, r8, and r9, in that order. [SysV] A.2.1.
~
~ Notice that rsi is our control stack, so we have to save it (for
~ syscalls with at least two parameters). We can use the value stack to do
~ that, since rsp is preserved, or we can use one of the other registers. We
~ don't ourselves save other registers because our caller should do that, if
~ it cares.
~
~ In the kernel source, you may find the following files useful to
~ reference:
~
~ * arch/x86/entry/syscalls/syscall_64.tbl
~ * include/linux/syscalls.h
~ * include/linux/compat.h
~
~ Don't be confused by tools/scripts/syscall.tbl, it's not for x86.
~
~ This file loads early, before dynamic.e. So, although it superficially
~ appears to be able to do allocation and high-level flow control, those come
~ from the transforms, which means they're shallow implementations. The stuff
~ here can't be moved later, because input.e relies on it, and the dynamic
~ stuff relies on that. So, there's additional Linux functionality in another
~ file that loads later, linux-dynamic.e.
~ (call number -- return value)
: syscall-0
[ here @
:rax pop-reg64 ~ syscall number
syscall
:rax push-reg64 ~ return value
here ! ] ;asm
~ (first param, call number -- return value)
: syscall-1
[ here @
:rax pop-reg64 ~ syscall number
:rdi pop-reg64 ~ first param
syscall
:rax push-reg64 ~ return value
here ! ] ;asm
~ (first param, second param, call number -- return value)
: syscall-2
[ here @
:rsi :rbx mov-reg64-reg64 ~ save rsi
:rax pop-reg64 ~ syscall number
:rsi pop-reg64 ~ second param
:rdi pop-reg64 ~ first param
syscall
:rbx :rsi mov-reg64-reg64 ~ restore rsi
:rax push-reg64 ~ return value
here ! ] ;asm
~ (first param, second param, third param, call number -- return value)
: syscall-3
[ here @
:rsi :rbx mov-reg64-reg64 ~ save rsi
:rax pop-reg64 ~ syscall number
:rdx pop-reg64 ~ third param
:rsi pop-reg64 ~ second param
:rdi pop-reg64 ~ first param
syscall
:rbx :rsi mov-reg64-reg64 ~ restore rsi
:rax push-reg64 ~ return value
here ! ] ;asm
~ (first param, second param, third param, fourth param, call number
~ -- return value)
: syscall-4
[ here @
:rsi :rbx mov-reg64-reg64 ~ save rsi
:rax pop-reg64 ~ syscall number
:r10 pop-extrareg64 ~ fourth param
:rdx pop-reg64 ~ third param
:rsi pop-reg64 ~ second param
:rdi pop-reg64 ~ first param
syscall
:rbx :rsi mov-reg64-reg64 ~ restore rsi
:rax push-reg64 ~ return value
here ! ] ;asm
~ (first param, second param, third param, fourth param, fifth param,
~ call number -- return value)
: syscall-5
[ here @
:rsi :rbx mov-reg64-reg64 ~ save rsi
:rax pop-reg64 ~ syscall number
:r8 pop-extrareg64 ~ fifth param
:r10 pop-extrareg64 ~ fourth param
:rdx pop-reg64 ~ third param
:rsi pop-reg64 ~ second param
:rdi pop-reg64 ~ first param
syscall
:rbx :rsi mov-reg64-reg64 ~ restore rsi
:rax push-reg64 ~ return value
here ! ] ;asm
~ (first param, second param, third param, fourth param, fifth param,
~ sixth param, call number -- return value)
: syscall-6
[ here @
:rsi :rbx mov-reg64-reg64 ~ save rsi
:rax pop-reg64 ~ syscall number
:r9 pop-extrareg64 ~ sixth param
:r8 pop-extrareg64 ~ fifth param
:r10 pop-extrareg64 ~ fourth param
:rdx pop-reg64 ~ third param
:rsi pop-reg64 ~ second param
:rdi pop-reg64 ~ first param
syscall
:rbx :rsi mov-reg64-reg64 ~ restore rsi
:rax push-reg64 ~ return value
here ! ] ;asm
~ Raw system calls
~ ~~~~~~~~~~~~~~~~
~ This does the Linux exit() system call, passing it an exit code taken
~ from the stack. It does not return.
~
~ (exit code -- *)
: sys-exit 60 syscall-1
~ In the event we're still here, let's minimize confusion.
crash ;
~ This does the Linux write() system call, passing it an address from the
~ top of the stack and a length from the second position on the stack. It
~ uses a Unix file descriptor, which is a non-negative integer, to tell it
~ where to write to. For stdout, use fd 1.
~
~ (file descriptor, base address, length to write -- result code or length)
: sys-write 1 syscall-3 ;
~ (file descriptor, base address, length to read -- result code or length)
: sys-read 0 syscall-3 ;
~ (filename, flags, mode -- result code or file descriptor)
: sys-open 2 syscall-3 ;
~ (file descriptor -- result code)
: sys-close 3 syscall-1 ;
|