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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
|
# practicum week 1
> ik heb de opdrachten met behulp van g++ als compiler en gdb als debugger
> gemaakt, dus sommige registernamen of syntax kan mogelijk niet overeenkomen
> met wat er verwacht wordt.
## practicum setup
```bash
$ make
g++ -c -g main.cpp -o main.o
g++ main.o -lstdc++ -o main
$ gdb main
Reading symbols from main...
(gdb) br main
Breakpoint 1 at 0x1181: file main.cpp, line 9.
(gdb) run
Starting program: /home/loek/docs/repos/os-huiswerk/os2w1/main
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
Breakpoint 1, main () at main.cpp:9
9 int a = 5;
(gdb)
```
## vraag 1
In gdb heet EIP `$pc`, en deze heeft nadat het programma is gepauzeerd in de
`main()` de hexadecimale waarde `0x555555555181`.
```
(gdb) print $pc
$1 = (void (*)(void)) 0x555555555181 <main()+8>
```
## vraag 2
`0x7fffffffcb20`:
```
(gdb) print $sp
$2 = (void *) 0x7fffffffcb20
```
## vraag 3
De door g++ gegenereerde machinecode bevat bij mij geen 200 bytes extra, en de
initialisatie van de main functie is ook een stuk beknopter:
```
(gdb) disas /m main
Dump of assembler code for function main():
8 int main() {
0x0000555555555179 <+0>: push rbp
0x000055555555517a <+1>: mov rbp,rsp
0x000055555555517d <+4>: sub rsp,0x10
9 int a = 5;
=> 0x0000555555555181 <+8>: mov DWORD PTR [rbp-0x8],0x5
10 int b = 7;
0x0000555555555188 <+15>: mov DWORD PTR [rbp-0x4],0x7
11
12 show(a, b);
0x000055555555518f <+22>: mov edx,DWORD PTR [rbp-0x4]
0x0000555555555192 <+25>: mov eax,DWORD PTR [rbp-0x8]
0x0000555555555195 <+28>: mov esi,edx
0x0000555555555197 <+30>: mov edi,eax
0x0000555555555199 <+32>: call 0x5555555551c3 <_Z4showii>
13 swap(a, b);
0x000055555555519e <+37>: mov edx,DWORD PTR [rbp-0x4]
0x00005555555551a1 <+40>: mov eax,DWORD PTR [rbp-0x8]
0x00005555555551a4 <+43>: mov esi,edx
0x00005555555551a6 <+45>: mov edi,eax
0x00005555555551a8 <+47>: call 0x555555555234 <_Z4swapii>
14 show(a, b);
0x00005555555551ad <+52>: mov edx,DWORD PTR [rbp-0x4]
0x00005555555551b0 <+55>: mov eax,DWORD PTR [rbp-0x8]
0x00005555555551b3 <+58>: mov esi,edx
0x00005555555551b5 <+60>: mov edi,eax
0x00005555555551b7 <+62>: call 0x5555555551c3 <_Z4showii>
15
16 return 0;
0x00005555555551bc <+67>: mov eax,0x0
17 }
0x00005555555551c1 <+72>: leave
0x00005555555551c2 <+73>: ret
End of assembler dump.
```
In de meegeleverde assembly wordt `ebp` - 216 in het `edi` register gezet. Deze
waarde is de nieuwe stack pointer die na het volmaken van de nieuw gealloceerde
ruimte in het echte stack pointer register gezet wordt.
## vraag 4
Omdat ik de c++ code heb gecompileerd met debug symbols kan gdb automatisch van
`a` en `b` zien dat het 32-bits signed integers zijn, en gdb interpreteert
automatisch de data op het adres van de variabelen op de "goede" manier.
```
(gdb) print $sp
$1 = (void *) 0x7fffffffcb20
(gdb) print &a
$2 = (int *) 0x7fffffffcb28
(gdb) print &b
$3 = (int *) 0x7fffffffcb2c
(gdb) info locals
a = -137244042
b = 32767
```
## vraag 5
```
(gdb) br main.cpp:12
Breakpoint 2 at 0x55555555518f: file main.cpp, line 12.
(gdb) c
Continuing.
Breakpoint 2, main () at main.cpp:12
12 show(a, b);
(gdb) info locals
a = 5
b = 7
(gdb) x &a
0x7fffffffcb28: 0x00000005
(gdb) x &b
0x7fffffffcb2c: 0x00000007
```
## vraag 6
Aan het einde van de `swap()` functie zijn de waardes van `a` en `b` (lokaal
tot de `swap()` functie) daadwerkelijk aangepast. Dit zijn alleen niet dezelfde
`a` en `b` als in de `main()` functie, dus lijkt het alsof er niks is gebeurt
na de return instructie.
```
(gdb) disas /m swap
Dump of assembler code for function _Z4swapii:
23 void swap(int a, int b) {
0x0000555555555234 <+0>: push rbp
0x0000555555555235 <+1>: mov rbp,rsp
0x0000555555555238 <+4>: mov DWORD PTR [rbp-0x14],edi
0x000055555555523b <+7>: mov DWORD PTR [rbp-0x18],esi
24 int h;
25 h = a;
0x000055555555523e <+10>: mov eax,DWORD PTR [rbp-0x14]
0x0000555555555241 <+13>: mov DWORD PTR [rbp-0x4],eax
26 a = b;
0x0000555555555244 <+16>: mov eax,DWORD PTR [rbp-0x18]
0x0000555555555247 <+19>: mov DWORD PTR [rbp-0x14],eax
27 b = h;
0x000055555555524a <+22>: mov eax,DWORD PTR [rbp-0x4]
0x000055555555524d <+25>: mov DWORD PTR [rbp-0x18],eax
28 }
=> 0x0000555555555250 <+28>: nop
0x0000555555555251 <+29>: pop rbp
0x0000555555555252 <+30>: ret
End of assembler dump.
(gdb) print a
$1 = 7
(gdb) print b
$2 = 5
```
## vraag 7
Ik zie dat er nu voor elke toewijzing van `a`, `b`, en `h` een extra instructie
die het `rax` register gebruikt is toegevoegd. Ik zie ook dat het adres voor
`a` en `b` in de scope van de `main()` functie nu hetzelfde is als die binnen
de `swap()` functie.
```
(gdb) disas /m swap
Dump of assembler code for function _Z4swapRiS_:
23 void swap(int &a, int &b) {
0x000055555555526b <+0>: push rbp
0x000055555555526c <+1>: mov rbp,rsp
0x000055555555526f <+4>: mov QWORD PTR [rbp-0x18],rdi
0x0000555555555273 <+8>: mov QWORD PTR [rbp-0x20],rsi
24 int h;
25 h = a;
0x0000555555555277 <+12>: mov rax,QWORD PTR [rbp-0x18]
0x000055555555527b <+16>: mov eax,DWORD PTR [rax]
0x000055555555527d <+18>: mov DWORD PTR [rbp-0x4],eax
26 a = b;
=> 0x0000555555555280 <+21>: mov rax,QWORD PTR [rbp-0x20]
0x0000555555555284 <+25>: mov edx,DWORD PTR [rax]
0x0000555555555286 <+27>: mov rax,QWORD PTR [rbp-0x18]
0x000055555555528a <+31>: mov DWORD PTR [rax],edx
27 b = h;
0x000055555555528c <+33>: mov rax,QWORD PTR [rbp-0x20]
0x0000555555555290 <+37>: mov edx,DWORD PTR [rbp-0x4]
0x0000555555555293 <+40>: mov DWORD PTR [rax],edx
28 }
0x0000555555555295 <+42>: nop
0x0000555555555296 <+43>: pop rbp
0x0000555555555297 <+44>: ret
End of assembler dump.
```
## vraag 8
```
(gdb) disas recursief
Dump of assembler code for function _Z9recursiefi:
0x0000000000001189 <+0>: push rbp
0x000000000000118a <+1>: mov rbp,rsp
0x000000000000118d <+4>: sub rsp,0x10
0x0000000000001191 <+8>: mov DWORD PTR [rbp-0x4],edi
0x0000000000001194 <+11>: mov eax,DWORD PTR [rbp-0x4]
0x0000000000001197 <+14>: sub eax,0x1
0x000000000000119a <+17>: mov edi,eax
0x000000000000119c <+19>: call 0x1189 <_Z9recursiefi>
0x00000000000011a1 <+24>: mov edx,DWORD PTR [rbp-0x4]
0x00000000000011a4 <+27>: add eax,edx
0x00000000000011a6 <+29>: leave
0x00000000000011a7 <+30>: ret
End of assembler dump.
```
Op regel drie van de disassembly is een `sub rsp,0x10` instructie te zien, die
16 bytes van de stack pointer afhaalt (16 bytes allocatie). Deze bytes zullen
waarschijnlijk komen door 4 bytes voor het argument dat `recursief` weer
meegegeven wordt bij de volgende aanroep, 4 bytes padding, 4 bytes voor de
return waarde en weer 4 bytes padding.
## vraag 9
Ik heb het volgende programma geschreven om te testen hoe groot de stack kan
worden voordat het programma zichzelf laat crashen:
```c
volatile unsigned long i = 0;
void rec() {
i++;
rec();
}
int main() {
rec();
return 0;
}
```
Ik heb het programma een paar keer uitgevoerd onder een debugger om de
uiteidenlijke waarde van `i` te inspecteren, en er kwam altijd een waarde rond
523700 (decimaal) uit. Deze functie reserveert geen lokale stack variabelen:
```
(gdb) disas rec
Dump of assembler code for function _Z3recv:
0x0000000000001119 <+0>: push rbp
0x000000000000111a <+1>: mov rbp,rsp
0x000000000000111d <+4>: mov rax,QWORD PTR [rip+0x2ef4] # 0x4018 <i>
0x0000000000001124 <+11>: add rax,0x1
0x0000000000001128 <+15>: mov QWORD PTR [rip+0x2ee9],rax # 0x4018 <i>
0x000000000000112f <+22>: call 0x1119 <_Z3recv>
0x0000000000001134 <+27>: nop
0x0000000000001135 <+28>: pop rbp
0x0000000000001136 <+29>: ret
End of assembler dump.
```
Zonder het reserveren van stackruimte per aanroep, kost elke aanroep alleen nog
8 bytes voor het return adres. De maximale stack grootte zal dan ongeveer 4 MB
zijn. De eerdere `recursief()` functie kost in totaal 32 bytes per aanroep (16
voor lokale variabelen, 8 voor return adres en nog 8 voor return waarde). Dat
zou betekenen dat de `recursief()` functie na ongeveer 130000 keer de stack
overvol zou maken.
|