diff options
-rw-r--r-- | os2w1/.gitignore | 2 | ||||
-rw-r--r-- | os2w1/main.cpp | 28 | ||||
-rw-r--r-- | os2w1/makefile | 17 | ||||
-rw-r--r-- | os2w1/practicum.md | 291 |
4 files changed, 338 insertions, 0 deletions
diff --git a/os2w1/.gitignore b/os2w1/.gitignore new file mode 100644 index 0000000..f0c9b81 --- /dev/null +++ b/os2w1/.gitignore @@ -0,0 +1,2 @@ +*.o +main diff --git a/os2w1/main.cpp b/os2w1/main.cpp new file mode 100644 index 0000000..b7d4663 --- /dev/null +++ b/os2w1/main.cpp @@ -0,0 +1,28 @@ +#include <iostream> + +using namespace std; + +void show(int m, int n); +void swap(int a, int b); + +int main() { + int a = 5; + int b = 7; + + show(a, b); + swap(a, b); + show(a, b); + + return 0; +} + +void show(int m, int n) { + cout << "Getallen: " << m << ", " << n << endl; +} + +void swap(int a, int b) { + int h; + h = a; + a = b; + b = h; +} diff --git a/os2w1/makefile b/os2w1/makefile new file mode 100644 index 0000000..258d688 --- /dev/null +++ b/os2w1/makefile @@ -0,0 +1,17 @@ +CPP = g++ +LD = g++ + +LFLAGS += -lstdc++ +CFLAGS += -g + +all: main + +main: main.o + $(LD) $^ $(LFLAGS) -o $@ + +%.o: %.cpp + $(CPP) -c $(CFLAGS) $< -o $@ + +clean: + $(RM) main.o main + diff --git a/os2w1/practicum.md b/os2w1/practicum.md new file mode 100644 index 0000000..749facd --- /dev/null +++ b/os2w1/practicum.md @@ -0,0 +1,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. + |