Laborator 1 - Introducere

New lab is translated in English and available here:

https://linux-kernel-labs.github.io/master/labs/introduction.html

Please always try to use the new labs.

If you find any problem (typo, bad wording, broken links, etc), please do the following

OR

Obiectivele laboratorului

  • prezentarea regulilor și modului de desfășurare a laboratorului de Sisteme de Operare 2
  • prezentarea suportului de laborator
  • prezentarea kernel-ului Linux și a resurselor aferente

Cuvinte cheie

  • kernel, kernel programming
  • Linux, vanilla, http://www.kernel.org
  • cscope, LXR
  • gdb, /proc/kcore, addr2line, dump_stack

Materiale ajutătoare

Desfășurarea laboratorului

Laboratorul de Sisteme de Operare 2 este un laborator de kernel programming și driver development. Obiectivele laboratorului sunt:

  • aprofundarea noțiunilor prezentate la curs
  • prezentarea interfețelor de programare la nivelul nucleului (kernel API)
  • dezvoltarea deprinderilor de documentare, dezvoltare și depanare pe un mediu freestanding
  • dobândirea de cunoștințe și deprinderi pentru dezvoltarea driverelor

Un laborator va prezenta un anumit set de noțiuni, aplicații și comenzi specifice unei problematici date. Laboratorul va debuta cu o prezentare (fiecare laborator va avea ca suport un set de slide-uri) (15 minute) urmând ca restul timpului să fie alocat rezolvării de exerciții de laborator (80 de minute).

Pentru o desfășurare cât mai bună a laboratorului, vă recomandăm parcurgea slide-urilor aferente. Pentru înțelegerea deplină a laboratorului, recomandăm parcurgerea suportului de laborator. Pentru aprofundare, folosiți documentația de suport.

Suport de laborator

cscope

Cscope este un program pentru parcurgerea eficientă a surselor C. Pentru a-l folosi, trebuie generată o bază de date cscope din sursele existente. Într-un tree Linux, este suficientă folosirea:

COMPILED_SOURCE=1 make ARCH=x86 cscope

Specificarea variabilei COMPILED_SOURCE cu valoarea 1 determină cscope să ia în considerare doar sursele compilate. Precizarea arhitecturii prin variabila ARCH este opțională, dar recomandată; altfel, unele funcții dependente de arhitectură vor apărea de mai multe ori în baza de date.

Cscope poate fi folosit și stand-alone, dar este mult mai util în combinație cu un editor. Pentru a folosi cscope cu Vim, este necesar să instalați ambele pachete și să adăugați următoarele linii în fișierul .vimrc (mașina din laborator are deja configurările făcute):

if has("cscope")
        " Look for a 'cscope.out' file starting from the current directory,
        " going up to the root directory.
        let s:dirs = split(getcwd(), "/")
        while s:dirs != []
                let s:path = "/" . join(s:dirs, "/")
                if (filereadable(s:path . "/cscope.out"))
                        execute "cs add " . s:path . "/cscope.out " . s:path . " -v"
                        break
                endif
                let s:dirs = s:dirs[:-2]
        endwhile
 
        set csto=0	" Use cscope first, then ctags
        set cst		" Only search cscope
        set csverb	" Make cs verbose
 
        nmap <C-\>s :cs find s <C-R>=expand("<cword>")<CR><CR>
        nmap <C-\>g :cs find g <C-R>=expand("<cword>")<CR><CR>
        nmap <C-\>c :cs find c <C-R>=expand("<cword>")<CR><CR>
        nmap <C-\>t :cs find t <C-R>=expand("<cword>")<CR><CR>
        nmap <C-\>e :cs find e <C-R>=expand("<cword>")<CR><CR>
        nmap <C-\>f :cs find f <C-R>=expand("<cfile>")<CR><CR>
        nmap <C-\>i :cs find i ^<C-R>=expand("<cfile>")<CR>$<CR>
        nmap <C-\>d :cs find d <C-R>=expand("<cword>")<CR><CR>
 
        " Open a quickfix window for the following queries.
        set cscopequickfix=s-,c-,d-,i-,t-,e-,g-
endif

Script-ul caută un fișier numit cscope.out în directorul curent, sau în directoarele părinte ale acestuia. Dacă Vim găsește acest fișier, puteți folosi combinația Ctrl+] sau Ctrl+\ g (combinația control-\, urmată de tasta g) pentru a sări direct la definiția cuvântului de sub cursor (funcție, variabilă, structură etc.). Similar, puteți folosi Ctrl+\ s pentru a merge la locurile unde este folosit cuvântul de sub cursor.

Puteți lua un fișier .vimrc cscope-enabled (and other goodies) de la https://github.com/ddvlad/cfg/blob/master/_vimrc. Următoarele indicații se bazează pe acest fișier, dar au listate și comenzile de bază vim care obțin același efect.

Dacă există mai multe rezultate (de obicei există) vă puteți deplasa între ele folosind F6 și F5 (:cnext și :cprev) sau deschizând o subfereastră nouă cu rezultatele, folosind :copen. Ca să închideți subfereastra folosiți comanda :cclose.

Pentru a vă întoarce la locația precedentă, folosiți Ctrl+o (o, nu zero). Comanda poate fi invocată de mai multe ori și funcționează chiar dacă cscope a schimbat fișierul pe care îl editați.

Pentru a merge la definiția unui simbol direct când porniți vim, folosiți vim -t task_struct. Sau, dacă ați deschis Vim și vreți ulterior să căutați un simbol după nume, puteți folosi comanda :cs find g <symbol_name> (unde <symbol_name> este numele simbolului).

Dacă ați găsit mai multe match-uri și dacă ați deschis o subfereastră cu toate match-urile (folosind :copen) și dacă sunteți în căutarea unui simbol de tip structură, este indicat să căutați în subfereastră (folosind /slash) caracterul { (acoladă deschisă).

Un sumar al comenzilor cscope îl puteți obține folosind :cs help.

Pentru mai multe informații, folosiți help-ul integrat al Vim: :h cscope sau :h copen.

cscope suportă următoarele tipuri de căutari:

  • 's' symbol: găsește toate referințele simbolului marcat de cursor
  • 'g' global: găsește definițiile globale ale simbolului marcat de cursor
  • 'c' calls: găsește toate apelurile funcției marcate de cursor
  • 't' text: găsește toate instanțele textului marcat de cursor
  • 'e' egrep: căutare egrep pentu cuvântul marcat de cursor
  • 'f' file: deschide fișierul cu numele marcat de cursor
  • 'i' includes: găsește fișiere care includ numele fișierului marcat de cursor
  • 'd' called: găsește funcțiile apelate de către funcția marcată de cursor

Mai multe informații găsiți aici.

Dacă sunteți utilizatori emacs, instalați pachetul xcscope-el și adăugați următoarele linii în ~/.emacs:

(require ‘xcscope)
(cscope-setup)

Aceste comenzi o să activeze cscope pentru modurile C și C++ automat. C-s s este prefixul de key bindings iar C-s s s este folosit pentru a căuta un symbol (dacă îl apelați atunci când sunteți cu cursorul pe un cuvânt îl va folosi pe acesta). Pentru mai multe detalii: https://github.com/dkogan/xcscope.el

Pentru o interfață mai simplă, Kscope este un frontend pentru cscope care foloseşte QT. Este lightweight, foarte rapid și foarte ușor de folosit. Permite căutare folosind expresii regulate, grafuri de apel etc. Kscope nu mai este, în momentul de fața, menținut. Există şi un port al versiunii 1.6 pentru Qt4 şi KDE 4 care păstrează integrarea editorului Kate şi este mai uşor de folosit decât ultima versiune prezentă pe SourceForge.

LXR Cross-Reference

LXR (LXR Cross-Reference) este un program care permite indexarea și referențierea simbolurilor din codul sursă a unui program prin intermediul unei interfețe web. Interfața web prezintă link-uri către locațiile din fișiere unde un simbol este definit sau utilizat. Site-ul de dezvoltare pentru LXR este acesta. Utilitare asemănătoare sunt OpenGrok și Gonzui.

Deși LXR a fost inițial destinat surselor kernel-ului de Linux, este folosit și la sursele utilitarelor de la Mozilla, Apache HTTP Server și FreeBSD.

Există o serie de site-uri care folosesc LXR pentru cross-referencing la sursele kernel-ului Linux, site-ul principal fiind site-ul inițial de dezvoltare. Care nu mai merge. Puteți folosi https://elixir.bootlin.com/.

LXR permite căutarea după un identificator (simbol), după un text liber sau după un nume de fișier. Principala caracteristică și, în același timp, principalul avantaj furnizat este posibilitatea de găsire facilă a declarației oricărui identificator global. Se realizează astfel foarte rapid accesul la declarații de funcții, variabile, macrodefiniții și codul poate fi parcurs facil. De asemenea, faptul că se poate detecta ce zone de cod sunt afectate în momentul modificării unei variabile sau funcții prezintă un real ajutor în faza de dezvoltare și debug.

SourceWeb

SourceWeb este un indexer pentru cod sursă C și C++. Acesta se folosește de framework-ul pus la dispoziție de compilatorul Clang pentru a indexa codul.

Principala diferență între cscope și SourceWeb este faptul că SourceWeb este, într-un fel, un compiler pass. SourceWeb nu indexează tot codul ci doar codul care a fost efectiv compilat de compilator. Astfel, dispar probleme precum ambiguități legate de care variantă a unei funcții definite în mai multe locuri e folosită. Dar asta înseamnă și că indexarea durează puțin mai mult deoarece fișierele compilate trebuie să mai treacă încă o dată prin indexer pentru a genera referințele.

Exemplu de folosire:

make oldconfig
sw-btrace make -j4
sw-btrace-to-compile-db
sw-clang-indexer --index-project
sourceweb index

sw-btrace este un script care adaugă biblioteca libsw-btrace.so în LD_PRELOAD. Astfel, biblioteca este încărcată de fiecare proces pornit de make (în principiu, compilatorul) și înregistrează liniile de comandă cu care au fost pornite procesele și generează un fișier btrace.log. Acest fișier este apoi preluat de sw-btrace-to-compile-db care-l convertește într-un format definit de clang: JSON Compilation Database.

Acest JSON Compilation Database rezultat din pașii de mai sus este apoi folosit de indexer care mai trece o dată prin fișierele compilate și generează indexul folosit de GUI.

Word of advice: nu indexați aceleași surse cu care lucrați, folosiți o copie pentru că SourceWeb nu are momentan capabilitatea de a regenera indexul pentru un singur fișier și va trebui să regenerați tot indexul.

Depanare

Depanarea unui kernel este un proces mult mai dificil decât depanarea unui program, pentru că nu există tocmai suportul sistemului de operare. De aceea, acest lucru se realizează de obicei prin intermediul a două calculatoare conectate pe interfețele seriale.

gdb (Linux)

O metodă de debug mai simplă pe Linux, dar cu multe lipsuri este depanarea locală folosind gdb, imaginea de kernel nearhivată (vmlinux) și /proc/kcore (imaginea în timp real a kernel-ului). Această metodă este folosită de obicei pentru inspecția kernel-ului și detectarea anumitor inconsistențe în timp ce acesta rulează. Metoda este utilă mai ales dacă s-a compilat kernel-ul cu optiunea -g de păstrare a informațiilor de debug. Nu pot fi folosite facilitățile de debug cunoscute cum sunt stabilirea de breakpoint-uri sau modificarea datelor.

Imaginea de kernel nearhivată oferă informații despre structurile de date și simbolurile existente:

so2@spook$ cd /usr/src/linux
so2@spook$ file vmlinux
vmlinux: ELF 32-bit LSB executable, Intel 80386, ...
so2@spook$ nm vmlinux | grep sys_call_table
c02e535c R sys_call_table
so2@spook$ cat System.map | grep sys_call_table
c02e535c R sys_call_table

Utilitarul nm este folosit pentru afișarea simbolurilor dintr-un cod obiect sau executabil. În cazul nostru, vmlinux este un fișier ELF. Alternativ se poate folosi System.map pentru afișarea informațiilor despre simbolurile din kernel.

Apoi folosim gdb pentru a inspecta simbolurile folosind imaginea nearhivată de kernel. O sesiune simplă de gdb este următoarea:

so2@spook$ cd /usr/src/linux
so2@spook$ gdb --quiet vmlinux
Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) x/x 0xc02e535c
0xc02e535c <sys_call_table>:    0xc011bc58
(gdb) x/16 0xc02e535c
0xc02e535c <sys_call_table>:    0xc011bc58      0xc011482a      0xc01013d3     0xc014363d
0xc02e536c <sys_call_table+16>: 0xc014369f      0xc0142d4e      0xc0142de5     0xc011548b
0xc02e537c <sys_call_table+32>: 0xc0142d7d      0xc01507a1      0xc015042c     0xc0101431
0xc02e538c <sys_call_table+48>: 0xc014249e      0xc0115c6c      0xc014fee7     0xc0142725
(gdb) x/x sys_call_table
0xc011bc58 <sys_restart_syscall>:       0xffe000ba
(gdb) x/x &sys_call_table
0xc02e535c <sys_call_table>:    0xc011bc58
(gdb) x/16 &sys_call_table
0xc02e535c <sys_call_table>:    0xc011bc58      0xc011482a      0xc01013d3     0xc014363d
0xc02e536c <sys_call_table+16>: 0xc014369f      0xc0142d4e      0xc0142de5     0xc011548b
0xc02e537c <sys_call_table+32>: 0xc0142d7d      0xc01507a1      0xc015042c     0xc0101431
0xc02e538c <sys_call_table+48>: 0xc014249e      0xc0115c6c      0xc014fee7     0xc0142725
(gdb) x/x sys_fork
0xc01013d3 <sys_fork>:  0x3824548b
(gdb) disass sys_fork
Dump of assembler code for function sys_fork:
0xc01013d3 <sys_fork+0>:        mov    0x38(%esp),%edx
0xc01013d7 <sys_fork+4>:        mov    $0x11,%eax
0xc01013dc <sys_fork+9>:        push   $0x0
0xc01013de <sys_fork+11>:       push   $0x0
0xc01013e0 <sys_fork+13>:       push   $0x0
0xc01013e2 <sys_fork+15>:       lea    0x10(%esp),%ecx
0xc01013e6 <sys_fork+19>:       call   0xc0111aab <do_fork>
0xc01013eb <sys_fork+24>:       add    $0xc,%esp
0xc01013ee <sys_fork+27>:       ret
End of assembler dump.

Se observă că s-a folosit ca parametru pentru gdb imaginea de kernel nearhivată care rezidă în rădăcina surselor după compilare.

Câteva comenzi utilizate pentru debugging cu gdb sunt:

  • x - Este folosită pentru afișarea conținutului zonei de memorie a cărei adresă este primită ca parametru (această adresă poate fi valoarea unei adrese fizice, un simbol sau adresa unui simbol). Poate primi ca parametri (precedați de /): formatul în care afișează datele (x pentru hexazecimal, d pentru zecimal, etc.), câte unități de memorie se afișează și dimensiunea unei unități de memorie.
  • disassemble - Este folosită pentru dezasamblarea unei funcții.
  • p - Este folosită pentru evaluarea și afișarea valorii unei expresii. Se poate specifica formatul în care se afișează datele (/x pentru hexazecimal, /d pentru zecimal, etc.).

Analiza imaginii de kernel este o analiză statică. Dacă dorim o analiză dinamică (o analiză a kernel-ului așa cum rulează el) vom folosi /proc/kcore; acesta este o imagine dinamică (în memorie) a kernel-ului.

so2@spook$ gdb /usr/src/linux/vmlinux /proc/kcore
Core was generated by `root=/dev/hda3 ro'.
#0  0x00000000 in ?? ()
(gdb) p sys_call_table
$1 = -1072579496
(gdb) p /x sys_call_table
$2 = 0xc011bc58
(gdb) p /x &sys_call_table
$3 = 0xc02e535c
(gdb) x/16 &sys_call_table
0xc02e535c <sys_call_table>:    0xc011bc58      0xc011482a      0xc01013d3     0xc014363d
0xc02e536c <sys_call_table+16>: 0xc014369f      0xc0142d4e      0xc0142de5     0xc011548b
0xc02e537c <sys_call_table+32>: 0xc0142d7d      0xc01507a1      0xc015042c     0xc0101431
0xc02e538c <sys_call_table+48>: 0xc014249e      0xc0115c6c      0xc014fee7     0xc0142725

Folosirea imaginii dinamice a kernel-ului este utilă pentru detectarea de rootkit-uri.

Obținerea unui stack trace

Uneori, veți dori informații despre calea de execuție prin care se ajunge într-o anumită situație. Puteți determina această informație folosind cscope sau LXR, dar unele funcții sunt apelate din foarte multe căi de execuție, ceea ce face dificil raționamentul.

În asemenea situații este utilă obținerea unui stack trace, lucru simplu de făcut folosind funcția dump_stack().

Documentație

Dezvoltarea kernel-ului are un grad sporit de dificultate raportat la programarea din user space. API-ul diferit, complexitatea subsistemelor kernel-ului necesită o etapă de pregătire suplimentară. Documentația asociată este destul de eterogenă, fiind nevoie de inspectarea mai multor surse pentru a avea o înțelegere completă a unui aspect.

Principalele avantaje ale kernel-ului Linux sunt accesul la surse și sistemul deschis de dezvoltare. Drept urmare, Internet-ul oferă un număr mult mai mare de resurse de documentare a kernel-ului.

Câteva link-uri utile sunt prezentate mai jos:

Link-urile nu sunt exhaustive. Folosirea Internet-ului și a surselor este esențială.

Exerciții

0. Înscriere pe lista de discuții

Urmăriți informațiile de pe pagina Listă de discuții pentru a vă abona la lista de discuții.

Precizări

  • În general, pașii pentru dezvoltarea unui modul de kernel sunt următorii:
    • editarea codului sursă al modulului (pe mașina fizică);
    • compilarea modulului (pe mașina fizică);
    • regenerarea imaginii minimale pentru mașina virtuală. Această imagine include kernel-ul, modulul vostru, busybox și eventual programe de test. Modulul vostru și programele de test trebuie puse în directorul /home/student/so2/qemu-so2/fsimg/root/.
    • pornirea mașinii virtuale folosind qemu. Acest pas și cel precedent se execută automat lansând make.
    • rularea testelor în mașina virtuală.
  • În cazul folosirii cscope, generați un fișier cscope.out, folosind: COMPILED_SOURCE=1 make ARCH=x86 cscope.

Înainte de a rezolva un subpunct, citiți cu atenție toate bullet-urile acestuia.

1. Boot-area mașinii virtuale

Pe scurt, infrastructura mașinii virtuale:

  • /home/student/so2/linux - sursele kernel-ului Linux, de care aveți nevoie pentru a compila module. Directorul include și fișierul cscope.out, pentru inspectarea facilă a surselor.
  • /home/student/so2/linux/tools/labs - fișiere suplimentare, necesare pentru rularea mașinii virtuale, laborator și teme

Pentru a rula mașina virtuală este suficient să intrați în directorul so2/linux/tools/labs și să rulați comanda make boot:

student@eg106:~$ cd so2/linux/tools/labs
student@eg106:~/so2/linux/tools/labs$ make boot

Implicit, nu există suport grafic (deci nu veți vedea un prompt), dar vă puteți conecta la o consola suplimentară oferită de mașina virtuală (în alt terminal) folosind screen sau minicom:

student@eg106:~/so2/linux/tools/labs$ minicom -D serial.pts

<apăsați enter>

qemux86 login: 
Poky (Yocto Project Reference Distro) 2.3 qemux86 /dev/hvc0

Alternativ, puteți porni mașina virtuală cu suport grafic:

student@eg106:~/so2/linux/tools/labs$ QEMU_DISPLAY=sdl make boot

Pentru accesarea mașinii virtuale, la prompt-ul apărut, introduceți numele de utilizator root; nu este nevoie de introducerea parolei. Veți accesa mașina virtuală cu permisiunile contului root.

2. Adăugarea și utilizarea unui disc virtual

Dacă nu aveți fișierul mydisk.img, îl puteți descărca de la adresa http://elf.cs.pub.ro/so2/res/laboratoare/mydisk.img.

În subdirectorul so2/linux/tools/labs/ aveți un nou disc de mașină virtuală în cadrul fișierului mydisk.img. Vrem să adăugăm discul la mașina virtuală și să îl folosim în cadrul mașinii virtuale.

Editați fișierul qemu/Makefile pentru a adăuga, în variabila QEMU_OPTS, parametrul -drive file=qemu/mydisk.img,if=virtio,format=raw. Rulați make pentru a boota mașina virtuală.

Este posibil să existe deja adăugate alte două discuri (disk1.img și disk2.img). În acest caz, adăugați noul disc înainte de disk1.img

În cadrul mașinii virtuale configurați accesul la disc.

Nu trebuie să creați manual intrarea aferentă noului disc în /dev pentru că mașina virtuală folosește devtmpfs. Noul disk este accesibil în mașina virtuală la calea /dev/vdb.

Creați directorul /test și încercați să montați noul disc:

mkdir /test
mount /dev/vdb /test

Motivul pentru care nu putem monta discul este pentru că nu avem suport în kernel pentru sistemul de fișiere cu care este formatat discul mydisk.img. Va trebui să identificați sistemul de fișiere aferent discului mydisk.img și să compilați suport în kernel pentru acel sistem de fișiere.

Închideți mașina virtuală (închideți fereastra qemu, nu e nevoie să folosiți altă comandă). Folosiți comanda file pe mașina fizică pentru a afla cu ce sistem de fișiere este formatat fișierul mydisk.img. Veți identifica sistemul de fișiere btrfs.

Va trebui să activați suportul de btrfs în kernel și să îl recompilați.

Dacă în momentul executării comenzii make menuconfig vă apare eroare, este probabil că nu aveți instalat pachetul libncurses5-dev. Instalați-l folosind comanda

sudo apt-get install libncurses5-dev

Intrați în subdirectorul so2/linux. Executați make menuconfig și intrați în secțiunea File systems. Activați opțiunea Btrfs file system support. Va trebui să folosiți opțiune de tip builtin (nu modul) adică trebuie să apară <*> în dreptul opțiunii (nu <M>).

Salvați configurația realizată. Folosiți fișierul de configurare implicit .config.

În subdirectorul cu sursele nucleului (linux/) recompilați folosind comanda

make

Pentru a aștepta mai puțin, puteți utiliza opțiunea -j pentru a folosi mai multe job-uri în paralel. În general se recomandă un număr de procese cu 1 mai mare decât numărul procesoarelor:

make -j5

După ce se încheie recompilarea, reporniți mașina virtuală QEMU: adică lansați comanda make în subdirectorul so2/linux/tools/labs/. Nu este nevoie să copiați nimic, pentru că mașina virtuală folosește implicit fișierul bzImage generat (imaginea kernel-ului pe care tocmai l-ați recompilat).

În cadrul mașinii virtuale QEMU repetați operațiunile de mkdir și mount. Având suport pentru sistemul de fișiere btrfs, acum mount se va termina cu succes.

Afișați conținutul fișierului din directorul /test.

În elaborarea temelor nu este necesar să recompilați kernel-ul, veți folosi doar module. Totuși, este important să fiți familiari cu configurarea și recompilarea unui kernel.

Dacă totuși aveți de gând să recompilați kernel-ul, faceți un back-up fișierului bzImage (din linux/arch/x86/boot/bzImage). Astfel veți putea reveni la setup-ul inițial pentru a avea un mediu identic cu vmchecker.

3. GDB și qemu

Putem investiga în timp real și depana mașina virtuală QEMU, folosind gdb.

Puteți folosi plugin-ul de gdb GDB Dashboard pentru o interfață mai prietenoasă (gdb trebuie sa fie compilat cu suport pentru Python). Pentru instalare, e suficient să rulați:

wget -P ~ git.io/.gdbinit

Pentru aceasta pornim, în primă fază, mașina virtuală QEMU. Apoi, ne putem conecta cu gdb la mașina vituală QEMU aflată în rulare, folosind comanda

make gdb

Am folosit comanda qemu cu parametrul -s, ceea ce înseamnă că ascultă pe portul 1234 de la GDB. Putem face debugging folosind un target remote pentru GDB. Makefile-ul existent are grijă de detalii.

Când atașați un debugger unui proces, procesul este suspendat. Puteți pune breakpoints și inspecta starea curentă a procesului.

Atașați-vă la mașina virtuală qemu (folosind comanda make gdb in directorul tools/labs) și puneți un breakpoint în funcția sys_access folosind în consola GDB comanda

break sys_access

În acest moment mașina virtuală este suspendată. Pentru a continua execuția ei (până la eventualul apel al funcției sys_access) folosiți, în consola GDB comanda

continue

În acest moment mașina virtuală este activă și are consola utilizabilă. Pentru a genera un apel de sys_access, lansați o comandă ls. Observați că mașina virtuală a fost din nou suspendată de GDB și a apărut mesajul aferent de apel al sys_access în cadrul GDB.

Urmăriți execuția codului, folosind step instruction, continue sau next instruction. Probabil nu o să înțelegeți tot ce se întâmplă, utilizați comenzi precum list și backtrace pentru a urmări logic execuția.

La prompt-ul gdb, puteți apăsa ENTER (fără altceva) pentru a rula ultima comandă încă o dată.

4. GDB spelunking

Folosiți GDB pentru a afișa codul sursă al funcției care creează thread-uri de kernel (kernel_thread).

Puteți folosi GDB pentru analiza statică a imaginii de kernel folosind, în directorul cu sursele kernel-ului, o comandă de forma

gdb vmlinux

Parcurgeți secțiunea gdb (Linux) din laborator.

Folosiți GDB pentru a afla adresa variabilei jiffies în memorie și conținutul acesteia. Variabila jiffies reține numărul de tick-uri (bătăi de ceas) de la pornirea sistemului,.

Pentru a urmări valoarea variabilei jiffies folosiți analiza dinamică în GDB folosind comanda

make gdb

la fel ca la exercițiul anterior.

Parcurgeți secțiunea gdb (Linux) din laborator.

Variabila jiffies este pe 64 de biți. Puteți observa că adresa sa este identică cu cea a variabilei jiffies_64.

Ca să explorați conținutul unei variabile pe 64 de biți, folosiți, în GDB, construcția

x/gx &jiffies

Dacă doreați să afișați conținutul variabilei pe 32 de biți, ați folosi, în GDB, construcția

x/wx &jiffies

5. cscope spelunking

Folosiți cscope în directorul ~/so2/linux pentru a determina locul de definire al unor structuri sau funcții.

Fișierele index cscope sunt deja generate. Folosiți direct Vim și comenzile aferente pentru parcurgerea codului sursă. De exemplu, folosiți comanda

vim

pentru a deschide editorul Vim. Apoi, în cadrul editorului, folosiți comenzi precum :cs find g task_struct.

Determinați fișierul în care sunt definite următoarele tipuri de date:

  • struct task_struct
  • struct semaphore
  • struct list_head
  • spinlock_t
  • struct file_system_type

Pentru o structură se caută doar numele ei. Spre exemplu, în cazul struct task_struct se caută șirul task_struct.

De obicei veți obține mai multe match-uri caz în care:

  1. Listați toate match-urile folosind, în Vim, comanda :copen. Vă apare o fereastră secundară cu toate match-urile.
  2. Căutați match-ul potrivit (în care este definită structura) căutând după acoladă deschisă ({), un caracter sigur pe linia de definire a structurii. Pentru căutarea acoladei deschise folosiți, în Vim, construcția /{.
  3. Pe linia aferentă apăsați Enter ca să vă ajungă editorul în codul sursă unde e definită varibila.
  4. Închideți fereastra secundară folosind coamanda :cclose.

Determinați fișierul în care sunt declarate următoarele variabile globale la nivelul nucleului:

  • sys_call_table
  • file_systems
  • current
  • chrdevs

Pentru aceasta folosiți în Vim o comandă de forma :cs f g <symbol> (unde construcția <symbol> reprezintă numele simbolului căutat). Dacă simbolul nu este găsit, încercați căutarea textuala folosind comanda: :cs f t <text>.

Determinați fișierul în care sunt declarate următoarele funcții:

  • copy_from_user
  • vmalloc
  • schedule_timeout
  • add_timer

Pentru aceasta folosiți în Vim o comandă de forma :cs f g <symbol> (unde construcția <symbol> reprezintă numele simbolului căutat).

Parcurgeți secvența de structuri:

  1. struct task_struct
  2. struct mm_struct
  3. struct vm_area_struct
  4. struct vm_operations_struct

Adică parcurgeți din aproape în aproape structurile: accesați o structură și apoi găsiți câmpuri cu tipul de date al următoarei structuri, accesați-o pe aceasta etc. Rețineți în ce fișiere sunt definite; o să vă fie utile la alte laboratoare.

Pentru a căuta un simbol în Vim (cu suport cscope) atunci când sunteți plasați cu cursorul pe acesta, folosiți construcția Ctrl+].

Pentru a reveni în match-ul anterior (înante de căutare/salt) folosiți construcția Ctrl+o. Pentru a avansa în căutare (pentru a reveni la match-urile de dinainte de Ctrl+o) folosiți construcția Ctrl+i.

La fel ca mai sus, parcurgeți secvența de apeluri de funcții:

  1. bio_alloc
  2. bio_alloc_bioset
  3. bvec_alloc
  4. kmem_cache_alloc
  5. slab_alloc

Citiți secțiunile cscope sau LXR Cross-Reference din laborator.

so2/laboratoare/lab01.txt · Last modified: 2019/02/25 16:44 by razvan.deaconescu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0