This shows you the differences between two versions of the page.
so2:laboratoare:lab02:exercitii [2018/02/26 18:03] ionel.ghita [Laborator 2: Exerciții] |
so2:laboratoare:lab02:exercitii [2019/02/25 15:18] (current) ionel.ghita [Extra] |
||
---|---|---|---|
Line 102: | Line 102: | ||
* Variabila ''ignore_loglevel'' este folosită pentru a ignora configurațiile de afișare a mesajelor de logging ale nucleului. În mod normal se poate stabili un prag peste care mesajele sunt afișate la terminal. Folosirea ''ignore_loglevel'' duce la ignorarea acelui prag. | * Variabila ''ignore_loglevel'' este folosită pentru a ignora configurațiile de afișare a mesajelor de logging ale nucleului. În mod normal se poate stabili un prag peste care mesajele sunt afișate la terminal. Folosirea ''ignore_loglevel'' duce la ignorarea acelui prag. | ||
</hidden> | </hidden> | ||
- | |||
- | <note tip> | ||
- | Pentru ''cscope'' parcurgeți [[:so2:laboratoare:lab01#cscope|secțiunea cscope din primul laborator]]. | ||
- | </note> | ||
<note warning> | <note warning> | ||
Line 114: | Line 110: | ||
<note tip> | <note tip> | ||
- | Atunci când folosiți ''cscope'', va trebui să vă aflați în directorul ''so2/linux-4.9.11/'' din directorul home al utilizatorului ''student''. | + | Atunci când folosiți ''cscope'', va trebui să vă aflați în directorul ''so2/linux/'' din directorul home al utilizatorului ''student''. |
</note> | </note> | ||
Line 134: | Line 130: | ||
La fel, dacă ați găsit mai multe match-uri și dacă ai deschis o subfereastră cu toate match-urile (folosind '':copen'') și dacă sunteți în căutarea locului de definiție a unui macro, este indicat să căutați în subfereastră (folosind ''/'' -- //slash//) după șirul ''define'' (șirul care indică locul de definiție a unui macro). | La fel, dacă ați găsit mai multe match-uri și dacă ai deschis o subfereastră cu toate match-urile (folosind '':copen'') și dacă sunteți în căutarea locului de definiție a unui macro, este indicat să căutați în subfereastră (folosind ''/'' -- //slash//) după șirul ''define'' (șirul care indică locul de definiție a unui macro). | ||
+ | </note> | ||
+ | |||
+ | <note tip> | ||
+ | Pentru mai multe detalii despre ''cscope'' parcurgeți [[:so2:laboratoare:lab01#cscope|secțiunea cscope din primul laborator]]. | ||
</note> | </note> | ||
==== 1. [1p] Module ==== | ==== 1. [1p] Module ==== | ||
Pentru a putea lucra cu modulele de kernel, vom realiza următorii pași, descriși și [[:so2:laboratoare:lab02:exercitii|mai sus în pagină]]: | Pentru a putea lucra cu modulele de kernel, vom realiza următorii pași, descriși și [[:so2:laboratoare:lab02:exercitii|mai sus în pagină]]: | ||
- | - Vom compila modulul de kernel. Adică vom rula, în directorul în care se găsesc sursele și fișierele de tip ''Makefile'' și ''Kbuild'' aferente modulului, comanda:<code> | + | - Vom genera scheletul de laborator, rulând comanda <code bash>LABS=kernel_modules make skels</code> în directorul ''tools/labs''. Această comandă copiază fișierele necesare laboratorului în directorul ''tools/labs/skels''. |
- | make | + | - Vom compila modulele de kernel. |
- | </code> | + | |
- | - Vom copia modulul în directorul din care va fi generat sistemul de fișiere al mașinii virtuale. Adică vom folosi comanda ''cp <nume-modul>.ko ~/so2/qemu-so2/fsimg/root/modules/''. | + | <note important> |
- | - Vom porni mașina virtuală. Adică vom rula, din cadrul directorului aferent mașinii virtuale (adică ''~/so2/qemu-so2/'') comanda<code> | + | Până la rezolvarea exercițiului 3, o să primiți o eroare de compilare la modului ''3-error-mod''. Puteți evita problema ștergând directorul ''skels/kernel_modules/3-error-mod/''. |
- | make | + | </note> |
+ | |||
+ | Vom rula, în directorul ''tools/labs'', comanda <code bash>make build</code>. Această comandă compilează modulele din toate exercițiile din laborator. | ||
+ | - Vom copia modulele compilate în mașina virtuală. Adică vom folosi comanda ''make copy''. | ||
+ | - Vom porni mașina virtuală. Adică vom rula, din cadrul directorului aferent mașinii virtuale (adică ''~/so2/linux/tools/labs/'') comanda<code> | ||
+ | make boot | ||
</code> | </code> | ||
- | Realizați pașii de mai sus pentru modulul din subdirectorul ''1-2-test-mod/'' din arhiva laboratorului. Urmăriți fișierele din subdirector. Apoi urmați pașii de mai sus pentru a porni mașina virtuală cu fișierul modul ''hello_mod.ko'' în sistemul de fișiere al mașinii virtuale. În cadrul mașinii virtuale, fișierul modul se găsește în directorul ''/root/modules/''. | + | Ne propunem să testăm pe mașina virtuală modulul din directorul ''1-2-test-mod/'' din scheletul laboratorului. Urmăriți fișierele din subdirector. |
- | + | Urmați pașii de mai sus pentru a compila și copia modulele pe mașina virtuală, apoi testați modulul ''hello_mod.ko'' din directorul ''/home/root/skels/kernel_modules/1-2-test-mod''. | |
- | <note tip> | + | |
- | Parcurgeți secțiunea [[:so2:laboratoare:lab02#Compilarea modulelor de kernel|Compilarea modulelor de kernel]] din laborator. | + | |
- | </note> | + | |
- | Apoi urmați pașii uzuali în foosirea unui modul de kernel: | + | Apoi urmați **pașii uzuali** în folosirea unui modul de kernel: |
- Încărcați modulul în kernel. | - Încărcați modulul în kernel. | ||
- Listați modulele din kernel și verificați existența modulului curent. | - Listați modulele din kernel și verificați existența modulului curent. | ||
Line 159: | Line 161: | ||
<note tip> | <note tip> | ||
- | Parcurgeți secțiunea [[:so2:laboratoare:lab02#Încărcarea/descarcarea unui modul de kernel|Încărcarea/descarcarea unui modul de kernel]] din laborator. | + | Parcurgeți secțiunea [[:so2:laboratoare:lab02:exercitii#Testarea modulelor|Testarea modulelor]]. |
La descărcarea unui modul din kernel poate fi precizat doar numele modulului (fără extensie). | La descărcarea unui modul din kernel poate fi precizat doar numele modulului (fără extensie). | ||
</note> | </note> | ||
- | |||
- | Este indicat să facem curat în locul în care am lucrat. Pentru aceasta, pe mașina fizică, în subdirectorul ''1-2-test-mod/'' în care ați compilat sursele modulului, pentru a curăța directorul rulați comanda<code> | ||
- | make clean | ||
- | </code> | ||
==== 2. [1p] Printk ==== | ==== 2. [1p] Printk ==== | ||
Line 177: | Line 175: | ||
Citiți secțiunea [[:so2:laboratoare:lab02#Printk Debugging|Printk Debugging]] din laborator și urmăriți precizările referitoare la folosirea funcției ''printk''. | Citiți secțiunea [[:so2:laboratoare:lab02#Printk Debugging|Printk Debugging]] din laborator și urmăriți precizările referitoare la folosirea funcției ''printk''. | ||
- | Pentru aceasta va trebui să editați opțiunile de bootare din fișierul ''/home/student/so2/qemu-so2/Makefile'', și să adăugați opțiunea de boot ''ignore_loglevel'' pe linia care începe cu ''%%append root...%%'' | + | Pentru aceasta va trebui să editați opțiunile de bootare din fișierul ''tools/labs/qemu/Makefile'', și să adăugați opțiunea de boot ''ignore_loglevel'' pe linia care începe cu ''%%append root...%%'' |
</note> | </note> | ||
- | Compilați modulul. Încărcați și apoi descărcați modulul din kernel. Mesajele sunt afișate la consola mașinii virtuale. Dacă ați deschis pseudo-terminalul indicat de qemu (pentru ''char device redirected to /dev/pts/19 (label virtiocon0)'' folosiți ''minicom -D /dev/pts/19'') atunci veti observa mesajele și acolo. | + | Compilați modulul. Încărcați și apoi descărcați modulul din kernel. Mesajele sunt afișate la consola mașinii virtuale. Dacă ați deschis pseudo-terminalul indicat de qemu (pentru ''char device redirected to /dev/pts/19 (label virtiocon0)'' folosiți ''minicom -D /dev/pts/19'') sau, mai simplu, link-ul simbolic ''serial.pts'' (folosiți ''minicom -D serial.pts'') atunci veți observa mesajele și acolo. |
==== 3. [1p] Error ==== | ==== 3. [1p] Error ==== | ||
- | | + | |
+ | <note important> | ||
+ | Dacă ați șters directorul ''3-error-mod/'' pentru a evita eroarea de compilare, puteți genera doar scheletul pentru acest exercițiu folosind urmăroarea comandă: <code bash>LABS=kernel_modules/3-error-mod make skels</code> | ||
+ | </note> | ||
Intrați în directorul ''3-error-mod/''. Obțineți modulul de kernel asociat. De ce au apărut erori de compilare? **Hint**: Cu ce diferă acest modul de modulul precedent? | Intrați în directorul ''3-error-mod/''. Obțineți modulul de kernel asociat. De ce au apărut erori de compilare? **Hint**: Cu ce diferă acest modul de modulul precedent? | ||
Line 191: | Line 193: | ||
Intrați în directorul ''4-multi-mod/''. Inspectați fișierele sursă C: ''mod1.c'' și ''mod2.c''. Exemplul este unul academic (modulul 2 conține doar definiția unei funcții folosite de modulul 1). | Intrați în directorul ''4-multi-mod/''. Inspectați fișierele sursă C: ''mod1.c'' și ''mod2.c''. Exemplul este unul academic (modulul 2 conține doar definiția unei funcții folosite de modulul 1). | ||
- | Creați un fișier Kbuild care să conducă la crearea fișierului-modul ''multi_mod.ko'' pornind de la cele două fișiere sursă C. **Hint**: Citiți secțiunea [[:so2:laboratoare:lab02#Compilarea modulelor de kernel|Compilarea modulelor de kernel]] din laborator. | + | Modificați fișierul ''Kbuild'' astfel încât să conducă la crearea fișierului-modul ''multi_mod.ko'' pornind de la cele două fișiere sursă C. **Hint**: Citiți secțiunea [[:so2:laboratoare:lab02#Compilarea modulelor de kernel|Compilarea modulelor de kernel]] din laborator. |
Compilați, încărcați și descărcați modulul. Mesajele sunt afișate corespunzător la consolă. | Compilați, încărcați și descărcați modulul. Mesajele sunt afișate corespunzător la consolă. | ||
Line 232: | Line 234: | ||
==== 7. [1.5p] Proc Info ==== | ==== 7. [1.5p] Proc Info ==== | ||
- | Intrați în directorul ''7-list-proc/''. Creați un modul care să afișeze informații despre procesul curent. Numele modulului trebuie sa fie ''list_proc.ko''. | + | Intrați în directorul ''7-list-proc/''. Completați modulul astfel încât să afișeze informații despre procesul curent. Numele modulului trebuie rezultat este ''list_proc.ko''. |
- | Afișați process ID-ul (PID-ul procesului) și numele executabilului. Informațiile vor fi afișate atât la încărcarea cât și la descărcarea modulului. | + | Urmăriți comentariile marcate cu ''TODO''. Afișați process ID-ul (PID-ul procesului) și numele executabilului. Informațiile vor fi afișate atât la încărcarea cât și la descărcarea modulului. |
<note tip> | <note tip> | ||
- | Nu începeți de la zero. Copiați fișierele ''Makefile'' și ''Kbuild'' și fișierele sursă C din unul din directoarele anterioare și modificați-le corespnzător. | ||
- | |||
În kernel-ul Linux un proces este descris de structura [[http://lxr.free-electrons.com/source/include/linux/sched.h?v=3.13#L1042|struct task_struct]]. | În kernel-ul Linux un proces este descris de structura [[http://lxr.free-electrons.com/source/include/linux/sched.h?v=3.13#L1042|struct task_struct]]. | ||
- | Folositi [[http://lxr.free-electrons.com/|LXR]] sau ''cscope'' pentru a afla conținutul unei structuri din nucleu (în cazul de față ''struct task_struct''). | + | Folositi [[http://lxr.bootlin.com/|LXR]] sau ''cscope'' pentru a afla conținutul unei structuri din nucleu (în cazul de față ''struct task_struct''). |
Pentru a găsi câmpul structurii ce conține numele executabilului aferent, căutați șirul //executable//. | Pentru a găsi câmpul structurii ce conține numele executabilului aferent, căutați șirul //executable//. | ||
Line 253: | Line 253: | ||
Repetați apoi operația de încărcare/descărcare. Observați că diferă PID-urile proceselor afișate. Acest lucru se întâmplă pentru că la încărcarea modulului se creează un proces pornind de la executabilul ''/sbin/insmod'' iar la descărcarea modulului se creează un proces pornind de la executabilul ''/sbin/rmmod''. Procesele vor fi diferite. | Repetați apoi operația de încărcare/descărcare. Observați că diferă PID-urile proceselor afișate. Acest lucru se întâmplă pentru că la încărcarea modulului se creează un proces pornind de la executabilul ''/sbin/insmod'' iar la descărcarea modulului se creează un proces pornind de la executabilul ''/sbin/rmmod''. Procesele vor fi diferite. | ||
- | ==== 8. [2.5p] Kprobes ==== | ||
- | |||
- | **[0.5p]** Intrați în directorul ''8-kprobes/'' din arhiva de resurse a laboratorului. Urmăriți fișierul sursă ''kprobes.c''. Modulul folosește ''jprobes'' pentru a urmări apelul ''do_execve_common'', aferent apelului de sistem [[http://man7.org/linux/man-pages/man3/exec.3.html|exec]]. | ||
- | |||
- | Compilați și încărcați în mașina virtuală modulul de kernel rezultat (''kprobes.c''). Urmăriți mesajele de debug și comparați cu procesele existente în sistem. | ||
- | |||
- | <note> | ||
- | Este chiar exmplul din laborator din secțiunea [[:so2:laboratoare:lab02#Kprobes|Kprobes]]. | ||
- | </note> | ||
- | |||
- | **[2p]** Creați un modul care analizează valoarea de retur a funcției [[http://lxr.free-electrons.com/source/kernel/fork.c?v=4.9#L1904|_do_fork]]. La întoarcerea din funcție, afișați valoarea de retur, numele și pid-ul procesului părinte și pid-ul procesului curent. | ||
- | |||
- | <note tip> | ||
- | Revedeți secțiunea [[:so2:laboratoare:lab02#Kprobes|Kretprobes]]. | ||
- | |||
- | Puteți urmări și exemplul de cod din sursele nucleului din [[https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/samples/kprobes/kretprobe_example.c?h=v3.13|samples/kprobes/kretprobe_example.c]]. | ||
- | </note> | ||
- | |||
- | <note tip> | ||
- | Urmăriți comentariile marcate cu ''TODO'' din cadrul fișierului cod sursă ''kprobes.c''. | ||
- | </note> | ||
- | |||
- | <note tip> | ||
- | Pentru a afișa adresa de retur în handler-ul de ''kretprobe'' (adică în funcțion ''my_ret_handler'') folosiți funcția [[http://lxr.free-electrons.com/source/arch/x86/include/asm/ptrace.h?v=3.13#L82|regs_return_value]]. | ||
- | </note> | ||
- | |||
- | <note tip> | ||
- | Pentru a obține structura de tip ''struct task_struct *'' aferentă procesului părinte al procesului curent folosiți construcția ''%%current->parent%%''. | ||
- | </note> | ||
- | |||
- | <note> | ||
- | Procesul interceptat de ''kretprobe'' este shell-ul. În mașina virtuală procesul său părinte este procesul ''init''/''busybox'' (cu PID-ul ''1''). Valoarea întoarsă de apelul [[http://lxr.free-electrons.com/source/kernel/fork.c?v=4.9#L1904|_do_fork]] afișată în cadrul handler-ului ''kretprobe'' este PID-ul procesului copil proaspăt creat, așa cum se întâmplă și în user space în cazul apelului [[http://man7.org/linux/man-pages/man2/fork.2.html|fork]] pentru procesul părinte. | ||
- | </note> | ||
- | Puteți folosi comanda ''pstree -p'' pentru a afișa ierarhia de procese a sistemului și pentru a verifica astfel, informațiile afișate de handler-ul de ''kretprobe'' în modulul de kernel. Puteți, deasemenea, confirma acest lucru verificând pid-ul procesului din proba anterioară ( ''do_execveat_common''). | + | ==== Extra ==== |
- | ==== Extra: Pentru acasă ==== | + | |
**1. [1KP] KDB** | **1. [1KP] KDB** | ||
- | Intrați în directorul ''9-kdb/''. Activați KDB peste serială și intrați în modul KDB folosind SysRq. | + | Intrați în directorul ''8-kdb/''. Activați KDB peste serială și intrați în modul KDB folosind SysRq. |
- | Conectațivă la pseudo-terminalul conectat la virtiocon0 folosind minicom, configurați KDB pentru a folosi portul serial hvc0 (''echo hvc0 > /sys/module/kgdboc/parameters/kgdboc'') și activați-l folosind SysRq (Ctrl+O g). Analizați starea curentă a sistemului (''help'' pentru a vedea comenzile KDB disponibile). Continuați execuția kernelului folosind comand ''go''. | + | Conectați-vă la pseudo-terminalul conectat la virtiocon0 folosind minicom, configurați KDB pentru a folosi portul serial hvc0 (''echo hvc0 > /sys/module/kgdboc/parameters/kgdboc'') și activați-l folosind SysRq (Ctrl+O g). Analizați starea curentă a sistemului (''help'' pentru a vedea comenzile KDB disponibile). Continuați execuția kernelului folosind comand ''go''. |
Inserați modulul ''hello_kdb''. Modulul va simula un bug la scrierea în fișierul ''/proc/hello_kdb_bug''. Pentru a simula un bug folosiți comanda de mai jos: <code bash> | Inserați modulul ''hello_kdb''. Modulul va simula un bug la scrierea în fișierul ''/proc/hello_kdb_bug''. Pentru a simula un bug folosiți comanda de mai jos: <code bash> | ||
Line 301: | Line 267: | ||
Analizați stacktrace-ul și determinați codul care a generat bugul. Cum putem afla din KDB adresa la care a fost încărcat modulul? | Analizați stacktrace-ul și determinați codul care a generat bugul. Cum putem afla din KDB adresa la care a fost încărcat modulul? | ||
- | În paralel, folosiți GDB într-o nouă fereastră pentru a vizualiza codul pornind de la informațiile din KDB. ** Hint: ** Încărcați fișierul de simboluri. Folosiți ''info line''. | + | În paralel, folosiți GDB într-o nouă fereastră pentru a vizualiza codul pornind de la informațiile din KDB. |
+ | ** Hint: ** Încărcați fișierul de simboluri. Folosiți ''info line''. | ||
La scrierea în fișierul ''/proc/hello_kdb_break'', modulul va incrementa variabila ''kdb_write_address''. Intrați în KDB și setați un breakpoint pentru fiecare acces de scriere al variabilei ''kdb_write_address''. Reveniți în kernel pentru a declanșa o scriere folosind: <code bash> echo 1 > /proc/hello_kdb_break </code> | La scrierea în fișierul ''/proc/hello_kdb_break'', modulul va incrementa variabila ''kdb_write_address''. Intrați în KDB și setați un breakpoint pentru fiecare acces de scriere al variabilei ''kdb_write_address''. Reveniți în kernel pentru a declanșa o scriere folosind: <code bash> echo 1 > /proc/hello_kdb_break </code> | ||
Line 324: | Line 291: | ||
Porniți de la un modul existent. | Porniți de la un modul existent. | ||
- | Investigați structura [[http://lxr.free-electrons.com/source/include/linux/sched.h?v=3.13#L1042|struct task_struct]], structura [[http://lxr.free-electrons.com/source/include/linux/mm_types.h?v=3.13#L344|struct mm_struct]] și structura [[http://lxr.free-electrons.com/source/include/linux/mm_types.h?v=3.13#L240|struct vm_area_struct]]. O zonă de memorie este indicată de o structură de tipul | + | Investigați structura [[https://elixir.bootlin.com/linux/v4.15.7/source/include/linux/sched.h#L520|struct task_struct]], structura [[https://elixir.bootlin.com/linux/v4.15.7/source/include/linux/mm_types.h#L356|struct mm_struct]] și structura [[https://elixir.bootlin.com/linux/v4.15.7/source/include/linux/mm_types.h#L274|struct vm_area_struct]]. O zonă de memorie este indicată de o structură de tipul [[https://elixir.bootlin.com/linux/v4.15.7/source/include/linux/mm_types.h#L274|struct vm_area_struct]]. |
Să includeți header-ele în care sunt definite structurile necesare. | Să includeți header-ele în care sunt definite structurile necesare. | ||
Line 331: | Line 298: | ||
**4. [2KP] Dynamic Debugging** | **4. [2KP] Dynamic Debugging** | ||
- | Intrați în directorul ''10-dyndbg/'' și compilați modulul ''dyndbg.ko''. | + | Intrați în directorul ''9-dyndbg/'' și compilați modulul ''dyndbg.ko''. |
Familiarizați-vă cu sistemul de fișiere ''debugfs'' montat în ''/debug'' și analizați conținutul fișierului ''/debug/dynamic_debug/control''. Inserați modulul dyndbg.ko și observați noul conținut al fișierului ''dynamic_debug/control''. | Familiarizați-vă cu sistemul de fișiere ''debugfs'' montat în ''/debug'' și analizați conținutul fișierului ''/debug/dynamic_debug/control''. Inserați modulul dyndbg.ko și observați noul conținut al fișierului ''dynamic_debug/control''. |