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
153
154
155
156
157
158
159
160
161
162
163
164
|
~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ ~~ 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.
~ (call number -- return value)
: syscall-0
[ here @
:rax pop-reg64 ~ syscall number
syscall
:rax push-reg64 ~ return value
here ! ] ;asm
~ (call number, first param -- return value)
: syscall-1
[ here @
:rdi pop-reg64 ~ first param
:rax pop-reg64 ~ syscall number
syscall
:rax push-reg64 ~ return value
here ! ] ;asm
~ (call number, first param, second param -- return value)
: syscall-2
[ here @
:rsi :rbx mov-reg64-reg64 ~ save rsi
:rsi pop-reg64 ~ second param
:rdi pop-reg64 ~ first param
:rax pop-reg64 ~ syscall number
syscall
:rbx :rsi mov-reg64-reg64 ~ restore rsi
:rax push-reg64 ~ return value
here ! ] ;asm
~ (call number, first param, second param, third param -- return value)
: syscall-3
[ here @
:rsi :rbx mov-reg64-reg64 ~ save rsi
:rdx pop-reg64 ~ third param
:rsi pop-reg64 ~ second param
:rdi pop-reg64 ~ first param
:rax pop-reg64 ~ syscall number
syscall
:rbx :rsi mov-reg64-reg64 ~ restore rsi
:rax push-reg64 ~ return value
here ! ] ;asm
~ (call number, first param, second param, third param, fourth param
~ -- return value)
: syscall-4
[ here @
:rsi :rbx mov-reg64-reg64 ~ save rsi
:r10 pop-extrareg64 ~ fourth param
:rdx pop-reg64 ~ third param
:rsi pop-reg64 ~ second param
:rdi pop-reg64 ~ first param
:rax pop-reg64 ~ syscall number
syscall
:rbx :rsi mov-reg64-reg64 ~ restore rsi
:rax push-reg64 ~ return value
here ! ] ;asm
~ (call number, first param, second param, third param, fourth param,
~ fifth param -- return value)
: syscall-5
[ here @
:rsi :rbx mov-reg64-reg64 ~ save rsi
: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
:rax pop-reg64 ~ syscall number
syscall
:rbx :rsi mov-reg64-reg64 ~ restore rsi
:rax push-reg64 ~ return value
here ! ] ;asm
~ (call number, first param, second param, third param, fourth param,
~ fifth param, sixth param -- return value)
: syscall-6
[ here @
:rsi :rbx mov-reg64-reg64 ~ save rsi
: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
:rax pop-reg64 ~ syscall number
syscall
:rbx :rsi mov-reg64-reg64 ~ restore rsi
:rax push-reg64 ~ return value
here ! ] ;asm
~ This does the Linux exit() system call, passing it an exit code taken
~ from the stack. It does not return.
~
~ (exit code -- *)
: sys-exit
[ here @
60 :rax mov-reg64-imm64 ~ syscall number
:rdi pop-reg64 ~ exit code
syscall
~ In the event we're still here, let's minimize confusion.
hlt
~ This one, uniquely, doesn't need to be followed by the "next" macro. It
~ is, though, since ;asm does that anyway.
here ! ] ;asm
~ 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
~ writes to file descriptor 1, which is stdout.
~
~ For our length parameter, we can pop directly from the stack into rdx,
~ which directly becomes the syscall parameter. For our address parameter,
~ the syscall wants it in rsi, which we also care about, so we have to do a
~ little juggling.
~
~ (length to write, base address -- *)
: sys-write
[ here @
:rcx pop-reg64 ~ address from stack
:rdx pop-reg64 ~ length from stack, passed directly
:rsi push-reg64 ~ save rsi
1 :rax mov-reg64-imm64 ~ syscall number
1 :rdi mov-reg64-imm64 ~ file descriptor
:rcx :rsi mov-reg64-reg64 ~ pass address
syscall
:rsi pop-reg64 ~ restore rsi
here ! ] ;asm
~ (length to read, base address -- *)
: sys-read
[ here @
:rcx pop-reg64 ~ address from stack
:rdx pop-reg64 ~ length from stack, passed directly
:rsi push-reg64 ~ save rsi
0 :rax mov-reg64-imm64 ~ syscall number
0 :rdi mov-reg64-imm64 ~ file descriptor
:rcx :rsi mov-reg64-reg64 ~ pass address
syscall
:rsi pop-reg64 ~ restore rsi
:rax push-reg64 ~ return length
here ! ] ;asm
|