summaryrefslogtreecommitdiff
path: root/os2w1/practicum.md
blob: 749facdac170e37d43cbb2c0cd8e08d906256d5d (plain)
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.