- gdbserver acționează ca un agent la distanță al GDB pentru a controla procesele de pe o altă mașină prin TCP sau serial.
- Pentru a depana de la distanță, este esențial să compilați cu simboluriFolosește gdb-ul corespunzător și configurează corect căile pentru simboluri și fonturi.
- gdbserver oferă mod cu un singur proces și mod cu mai multe procese, integrându-se și cu WinDbg și QEMU pentru depanarea kernelului.
- Opțiuni precum --debug, sysroot și limitele de dimensiune a valorilor ajută la diagnosticarea problemelor și la stabilizarea sesiunilor.
Dacă programezi în C sau C++ în Linux și nu ai atins niciodată gdbserverPierzi unul dintre cele mai utile instrumente pentru depanarea proceselor de la distanță, fie pe un server, un sistem integrat sau chiar într-o mașină virtuală sau WSL. Departe de a fi ceva „magic” sau rezervat experților, gdbserver este pur și simplu un mic program care comunică cu gdb și controlează execuția procesului țintă.
Ideea cheie este foarte simplă.pe mașina pe care rulează fișierul binar pe care doriți să îl depanați ( ţintă) porniți gdbserver; pe computerul de serviciu ( gazdăPornești gdb sau chiar WinDbg cu suport pentru protocolul gdb. Ambele se conectează prin TCP sau un port serial, iar de acolo poți seta puncte de întrerupere, inspecta variabile, vizualiza stiva sau urmări execuția pas cu pas ca și cum programul ar rula pe propria mașină.
Ce este gdbserver și când are sens să îl folosim?

gdbserver este un „agent” de depanare la distanță pentru GNU gdbFuncția sa este foarte specifică: rulează pe mașina pe care rulează programul de analizat, controlează acel proces (sau procesele) și comunică cu un client gdb situat pe o altă mașină (sau pe aceeași) printr-o conexiune la distanță.
În utilizarea zilnică, gdbserver este utilizat în două scenarii tipiceSoftware de depanare care rulează în medii integrate (routere, plăci de bază cu Linux simplificat, dispozitive) IoTetc.) și depanarea proceselor pe mașini Linux la distanță, unde nu este convenabil sau pur și simplu nu este posibil să ai o bază de date gdb „grasă” cu toate bibliotecile și simbolurile.
La nivel practic, gdbserver gestionează sarcini precum Citește și scrie registre de proces și memorie, controlează execuția (continuă, întrerupe, parcurge pas cu pas), gestionează punctele de întrerupere și trimite toate aceste date către gdb folosind protocolul GDB remote. Această filozofie este foarte similară cu cea a instrumentelor precum OpenOCD, care acționează ca o punte între gdb și un hardware extern, cu diferența că gdbserver rulează pe același sistem pe care rulează fișierul binar.
Dacă proveniți din medii ferestre din De asemenea, este interesant de știut Depanatoarele precum WinDbg pot comunica cu un server gdb pe Linux, astfel încât puteți depana procesele utilizatorilor pe Linux din WinDbg folosind suportul de depanare la distanță prin protocolul gdb pe care Microsoft l-a încorporat în versiunile recente.
Cerințe de bază pentru depanare cu gdb și gdbserver

Înainte de a începe depanarea de la distanță, trebuie să înțelegeți relația gazdă/țintă.. ţintă Este mașina pe care rulează programul care trebuie depanat și unde va fi executat gdbserver; gazdă Aceasta este mașina de pe care veți rula gdb (sau WinDbg) și unde veți avea codul sursă și, de preferință, simbolurile de depanare.
Punctul de plecare esențial este compilarea fișierului binar cu simboluriÎn GCC sau g++ acest lucru se realizează cu steagul -gși de obicei este recomandabil să dezactivați și optimizările (de exemplu, cu -O0Acest lucru permite depanatorului să afișeze mai precis variabilele, macrocomenzile și structura codului. Pentru anumite macrocomenzi, puteți utiliza niveluri de depanare superioare, cum ar fi -g3.
Pe partea de gazdă veți avea nevoie de o versiune compatibilă de gdb cu arhitectura țintă. Pentru a depana un sistem încorporat cu arhitectură MIPS, ARM sau altă arhitectură, trebuie să utilizați gdb-ul lanțului de instrumente cross-toolchain corespunzător (de exemplu) arm-none-eabi-gdb o gdb-multiarch) și, dacă este necesar, configurați arhitectura și endianness-ul cu comenzi ca set arch y set endian.
În ceea ce privește conexiunea, gdbserver acceptă două tipuri principaleO legătură serială (foarte comună în hardware-ul embedded, prin UART) și TCP/IP, ceea ce este cel mai convenabil atunci când ținta se află în aceeași rețea sau este o mașină Linux accesibilă prin rețea. În ambele cazuri, comanda este utilizată din gdb. target remote pentru a se conecta la endpoint-ul expus de gdbserver.
Modalități de pornire a gdbserver: modul cu un singur proces și modul cu mai multe procese

gdbserver poate funcționa în două moduri principale Când vorbim despre depanare în modul utilizator: asociată direct cu un singur proces sau ca „server de procese” care permite listarea și atașarea la diferite procese de sistem.
În modul cu un singur proces Lansați gdbserver, specificând portul gazdă și programul de rulat. Într-un exemplu simplu pe o mașină desktop Linux, ați putea face ceva de genul:
comandă: gdbserver localhost:3333 foo
Cu această comandă, gdbserver pornește fișierul binar. foo și el rămâne ascultând la portul 3333Până când se conectează o bază de date gdb la distanță, programul rămâne oprit; când gdb se conectează cu target remote localhost:3333, procesul începe să fie controlat de către descrasher.
În modul multi-proces (server de procese), opțiunea este utilizată --multiÎn acest caz, gdbserver nu lansează direct niciun program, ci pur și simplu ascultă conexiunile primite și permite clientului (gdb sau WinDbg) să gestioneze ce proces să creeze sau la care să se atașeze:
comandă: gdbserver --multi localhost:1234
Când se lucrează cu WinDbg pe Linux, acest multi-mod este deosebit de interesant.Deoarece chiar din WinDbg poți lista procesele de pe sistemul la distanță, poți vedea PID-ul, utilizatorul și linia de comandă și le poți atașa la cea care te interesează, într-un mod similar cu cel în care se face cu serverul de procese. dbgsrv.exe pe Windows.
Depanare la distanță cu gdbserver și gdb pas cu pas
Să aducem asta cu picioarele pe pământ cu un exemplu foarte tipic.Depanați o aplicație simplă pe aceeași mașină (gazda și ținta se potrivesc) folosind gdbserver pentru a simula scenariul la distanță.
Mai întâi scrii și compilezi un mic programDe exemplu, o buclă absurdă care afișează un contor:
comandă: gcc -g foo.c -o foo
Cheia aici este steagul -gAceasta adaugă informațiile de depanare necesare la fișierul binar, astfel încât gdb să poată afișa linii de cod, nume de variabile, tipuri etc. Într-un mediu de compilare încrucișată „real”, ați face această compilare cu cross-toolchain-ul și apoi ați copia atât fișierul binar, cât și dependențele sale în țintă.
Următorul pas este să porniți gdbserver pe țintă.Dacă gazda și destinația sunt aceeași mașină, atunci:
comandă: gdbserver localhost:3333 foo
Veți vedea un mesaj similar cu „Proces foo creat; pid = XXXX; Ascultă pe portul 3333”. Aceasta indică faptul că gdbserver a creat procesul și așteaptă conectarea de către gdb. Dacă vă aflați pe un sistem în care sunt necesare mai multe privilegii (de exemplu, pentru a vă atașa la procesele de sistem), este posibil să fie nevoie să executați comanda cu sudoDar este întotdeauna înțelept să fii precaut atunci când acorzi permisiunea. rădăcină către desulfurizator.
Pe gazdă, porniți gdb specificând executabilul local (același care rulează pe țintă sau o copie identică cu simboluri):
comandă: gdb foo
Odată ajuns în gdb, stabiliți conexiunea la distanță cu:
comandă: target remote localhost:3333
În acel moment, gdb încarcă simbolurile din binarul local.Se sincronizează cu gdbserver și preia controlul asupra procesului care rulează efectiv sub gdbserver. De acolo, fluxul este cel obișnuit: comenzi precum break pentru a stabili puncte de rupere, continue, step, next, print pentru a inspecta variabilele, backtrace să vezi bateria etc.
Conectarea la procesele care rulează cu gdbserver
Nu vrei întotdeauna să lansezi programul de la zero.Adesea ești interesat să te alături unui proces care deja rulează (de exemplu, un httpd o routerun daemon de sistem sau un serviciu de producție).
Modelul tipic este de a utiliza opțiunea --attach de la gdbservertransmițând portul unde va asculta și PID-ul procesului țintă. De exemplu, pe un router unde ați copiat un gdbserver compilat pentru arhitectura sa, ați putea face:
comandă: gdbserver localhost:3333 --attach <pid_de_httpd>
Pe partea de gazdă, veți utiliza o versiune de gdb care suportă arhitectura routerului.de exemplu gdb-multiarchconfigurarea arhitecturii și a endianismului în prealabil:
comandă: set arch mips
set endian big
Apoi specificați fișierul local care conține simbolurile. al fișierului binar la distanță (de exemplu file httpd) și, dacă este necesar, îi spuneți lui gdb unde rulează de fapt fișierul binar pe țintă cu set remote exec-file /usr/bin/httpdÎn final, la fel ca înainte, te conectezi cu:
comandă: target remote 192.168.0.1:3333
Odată atașatPuteți seta puncte de întrerupere pentru anumite funcții (de exemplu break checkFirmware), continuați execuția și lăsați fluxul normal al programului (de exemplu, încărcarea firmware-ului din interfața web) să declanșeze punctul de întrerupere.
Utilizarea gdbserver cu WinDbg pe Linux
În ultimii ani, Microsoft a adăugat suport pentru depanarea proceselor Linux în WinDbg. Utilizarea gdbserver ca backend. Această funcționalitate este destinată scenariilor în care lucrați în Windows, dar codul rulează pe Linux (inclusiv WSL).
Pentru a depana un anumit proces Linux cu WinDbg folosind gdbserverFluxul ar fi cam așa: mai întâi localizați procesul țintă pe mașina Linux cu o comandă de genul ps -A (de exemplu a python3 care rulează), apoi lansați gdbserver pe țintă:
comandă: gdbserver localhost:1234 python3
Dacă mediul o cere, este posibil să fie nevoie să utilizați sudo gdbserver ...cu aceleași precauții de securitate ca întotdeauna. După ce gdbserver indică faptul că „Ascultă pe portul 1234”, în WinDbg accesați „Fișier / Conectare la depanator la distanță” și specificați un șir de conexiune de următorul tip:
comandă: gdb:server=localhost,port=1234
WinDbg folosește un mic „driver” de protocol gdb pentru a comunica cu gdbserver și, odată ce conexiunea este stabilită, aceasta rămâne oprită în punctul de cizmă a procesului. De acolo puteți utiliza ferestrele stivei, modulele, memoria, punctele de întrerupere, precum și comenzi precum k pentru a vedea bateria sau lm pentru a lista modulele (ținând cont de faptul că unele comenzi așteaptă formatul PE și nu ELF, așa că pot afișa date ciudate în anumite cazuri).
gdbserver și serverul de procese WinDbg
Pe lângă cazul unui singur proces, WinDbg se poate conecta la un server gdb care acționează ca un server de procese. să funcționeze mai similar cu modul în care funcționează cu procesele Windows la distanță. În acest mod, gdbserver este lansat cu --multi și fără un proces asociat:
comandă: sudo gdbserver --multi localhost:1234
Din WinDbg, alegeți „Fișier / Conectare la serverul de procesare” și reutilizezi șirul de conexiune gdb:server=localhost,port=1234Când conexiunea este activă, puteți lista procesele Linux disponibile și le puteți atașa la cel dorit sau chiar puteți lansa un proces nou.
Trebuie ținut cont de un detaliu subtil.WinDbg face distincția între „server de procese” și „țintă unică” în funcție de dacă gdbserver este deja atașat la un proces atunci când se conectează. Dacă ați lăsat gdbserver atașat la un proces, ați închis WinDbg și apoi ați încercat să vă reconectați, este posibil ca acesta să nu fie detectat ca server de procese și poate fi necesar să reporniți gdbserver.
Pentru a încheia o sesiune a serverului de procesareDe obicei, este suficientă simpla apăsare a tastelor CTRL+D în consola în care rulează gdbserver și oprirea depanării din WinDbg. În unele cazuri extreme, dacă există probleme de sincronizare, poate fi necesară închiderea completă a depanatorului și repornirea gdbserver de la zero.
Gestionarea simbolurilor și a codului sursă în depanarea la distanță
Una dintre cheile pentru a face depanarea la distanță convenabilă este rezolvarea corectă a părții de simboluri și fonturi.Fără simboluri, navigarea prin stivă sau setarea punctelor de întrerupere pentru anumite funcții devine o tortură.
În scenariile clasice gdb + gdbserver, este ideal să păstrați o copie a executabilului cu simboluri pe gazdă. (neeliminat) și arborele sursă. gdb nu necesită ca binarul la distanță să conțină simbolurile; este suficient ca fișierul local pe care îl încărcați cu file potrivește executabilul la distanță la nivel de offset.
În lumea depanării WinDbg și Linux, au apărut și servicii precum DebugInfoD.care expun simboluri și fonturi prin HTTP. WinDbg poate utiliza căi speciale de tipul DebugInfoD*https://debuginfod.elfutils.org amândoi .sympath la fel ca în .srcpath pentru a descărca simboluri DWARF la cerere și cod sursă pentru binarele Linux ELF.
Într-un exemplu specific cu WSL, unde codul utilizatorului este sub C:\Users\Bob\Ai putea să-i spui lui WinDbg:
comandă: .sympath C:\Users\Bob\
.srcpath C:\Users\Bob\
Și dacă doriți să utilizați DebugInfoD și pentru fișierele binare de sistem:
comandă: .sympath+ DebugInfoD*https://debuginfod.elfutils.org
.srcpath+ DebugInfoD*https://debuginfod.elfutils.org
Cu această configurație, când inspectați stiva sau introduceți funcții libcWinDbg poate încerca să descarce simbolurile DWARF corespunzătoare și, dacă serverul expune și codul, să afișeze sursa în detaliu considerabil, deși intern, lanțul de instrumente Windows nu gestionează ELF și DWARF la fel de „nativ” ca PE și PDB.
Exemplu practic: depanarea unui program C++ cu gdbserver și WinDbg
Un exemplu ilustrativ este o mică aplicație C++ care scrie un mesaj de salut pe ecran., compilat în WSL cu simboluri de depanare. Imaginați-vă un program care rezervă un std::array<wchar_t, 50> și copiază un mesaj mai lung în acesta, ceea ce duce la trunchierea textului și la apariția caracterelor ???? la final
După compilare cu ceva de genul:
comandă: g++ DisplayGreeting.cpp -g -o DisplayGreeting
Pornești gdbserver cu acel binar:
comandă: gdbserver localhost:1234 DisplayGreeting
În WinDbg te conectezi cu șirul de caractere gdb:server=localhost,port=1234 Și, odată ce sesiunea este stabilită și căile pentru simboluri și fonturi sunt configurate, setați un punct de întrerupere în DisplayGreeting!mainputeți folosi dx greeting pentru a inspecta matricea locală și a vedea dimensiunea acesteia (50 de poziții) și a verifica vizual în fila memorie sau în vizualizarea variabilelor cum este întrerupt mesajul de salut.
Frumusețea acestui exemplu constă în faptul că demonstrează că, chiar și fără suport complet pentru toate formatele ELF/DWARF în WinDbgPuteți parcurge stivele, inspecta tipurile, seta puncte de întrerupere după numele funcției și naviga prin cod C++ relativ confortabil folosind gdbserver ca backend la distanță.
Depanarea kernelului Linux cu qemu și gdb
gdbserver nu este utilizat doar în modul utilizator; există și scenarii foarte puternice în modul kernel.mai ales când combinați QEMU cu suportul de depanare. Deși aici rolul „gdbserver” este îndeplinit de propria opțiune a QEMU, abordarea este identică: un capăt rulează sistemul care urmează să fie depanat și deschide un port gdb; celălalt capăt este fie gdb, fie un depanator care comunică protocolul la distanță.
Pentru a depana kernelul, trebuie să îl compilați cu opțiuni specifice de depanare.: activează generarea informațiilor de depanare (CONFIG_DEBUG_INFO), scripturile kernelului GDB (CONFIG_GDB_SCRIPTS) și modul de depanare propriu al kernelului (CONFIG_DEBUG_KERNELDe asemenea, este important să dezactivați opțiunile care elimină simbolurile în timpul legăturii, cum ar fi „Eliminați simbolurile generate de asamblator în timpul legăturii”.
După compilare veți obține un fișier binar vmlinux „nedezbrăcat”care este cel pe care îl veți folosi din gdb. De asemenea, aveți nevoie de un fișier initramfs de bază, pe care îl puteți genera cu o comandă de genul:
comandă: mkinitramfs -o ramdisk.img
Apoi pornești QEMU cu parametrii de depanareUn exemplu tipic include opțiunea -gdb tcp::1234 pentru a deschide un endpoint la distanță compatibil cu gdb și -S astfel încât mașina virtuală să pornească în pauză de la început. De asemenea, specificați kernelul cu -kernel vmlinux, -initrd ramdisk.img, memoria cu -m 512 și de obicei redirecționezi consola către ttyS0 să gestioneze totul de la terminal.
Cu QEMU reținut în așteptarea gdbDe pe mașina gazdă, porniți gdb indicând către vmlinux și te conectezi cu target remote localhost:1234De acolo puteți seta puncte de întrerupere timpurii, de exemplu a hb start_kernelși controlează execuția cu comenzi precum c (continuare) și CTRL+C pentru a pune din nou pauză.
Modificări și nuanțe recente în gdb și gdbserver
În distribuțiile moderne precum Red Hat Enterprise Linux 8, există o serie de modificări în gdb și gdbserver care merită ținute cont.mai ales dacă folosești versiuni anterioare sau ai scripturi care analizează ieșirea depanatorului.
Pe de o parte, gdbserver pornește acum procesele „inferioare” folosind un shellLa fel ca gdb, aceasta permite extinderea variabilelor și substituțiile în linia de comandă. Dacă, din orice motiv, trebuie să dezactivați acest comportament, există setări specifice documentate în RHEL 8 pentru a reveni la modul anterior.
De asemenea, au fost eliminate sau modificate mai multe lucrurisuport de depanare pentru programele Java compilate cu gcj, modul de compatibilitate HP-UX XDB, comenzi precum set remotebaud (înlocuit de set serial baud) sau compatibilitatea cu un anumit format mai vechi de stabsÎn plus, numerotarea firelor de execuție nu mai este globală, ci pe fir „inferior” și apare ca inferior_num.thread_num, cu noi variabile de conveniență, cum ar fi $_gthread pentru a face referire la identificatorul global.
O altă caracteristică nouă relevantă este ajustarea max-value-sizeAceasta limitează cantitatea de memorie pe care gdb o poate aloca pentru afișarea conținutului unei valori. Valoarea implicită este de 64 KiB, așadar încercările de a imprima tablouri uriașe sau structuri masive pot duce la afișarea unui avertisment „valoare prea mare” în loc să se afișeze toată memoria disponibilă.
De asemenea, a fost ajustat modul în care gdb gestionează sysrootValoarea implicită este acum target:Aceasta înseamnă că, pentru procesele la distanță, va încerca mai întâi să găsească biblioteci și simboluri pe sistemul țintă. Dacă doriți să acorde prioritate simbolurilor locale, ar trebui să rulați set sysroot cu ruta care te interesează înainte de a face target remote.
În ceea ce privește istoricul comenzilor, variabila de mediu utilizată acum este GDBHISTSIZE în loc de HISTSIZEAcest lucru vă permite să reglați fin durata de timp pentru care doriți să păstrați comenzile pe care le-ați tastat în sesiunile de depanare, fără a interfera cu comportamentul altor aplicații care utilizează biblioteca de citire a liniilor.
Sfaturi pentru fluxul de lucru și depanare cu gdbserver
Pentru a avea un flux de lucru confortabil, există câteva modele care tind să funcționeze foarte bine. Când se dezvoltă pentru sisteme integrate sau servere la distanță, primul pas este automatizarea cât mai mult posibil a compilării simbolurilor și a implementării fișierelor binare către destinație. În acest fel, se știe întotdeauna ce versiune a executabilului rulează și se are copia simbolului disponibilă imediat pe gazdă.
În mediile cu multe nuclee de eroare, merită să înveți cum să utilizezi gdb în modul batch., cu steaguri precum --batch, --ex y -x pentru a lansa automat comenzi pe o listă de nuclee și a procesa backtrace-urile acestora din scripturi (de exemplu în PitonAcest lucru vă permite să filtrați rapid problemele repetate, erorile de grupare prin trasarea stivei etc.
Când apare o problemă cu conexiunea la distanță, opțiunea --debug gdbserver este cel mai bun prieten al tăuDacă porniți, de exemplu, un server de procesare cu:
comandă: gdbserver --debug --multi localhost:1234
Consola gdbserver va afișa urme detaliate ale ceea ce se întâmplă La nivel de protocol la distanță, aceasta include pachete primite, erori de formatare, probleme de deconectare etc. Acest lucru este foarte util atunci când serverul gdb se deconectează brusc, un proces se blochează imediat ce este setat un punct de întrerupere sau interfața grafică de depanare trimite ceva ce gdbserver nu înțelege.
În contexte precum un router TP-Link unde atașați gdbserver la un proces critic, cum ar fi httpdEste relativ comun ca anumite puncte de întrerupere să creeze condiții de concurență sau watchdog-uri care opresc procesul atunci când acesta rămâne „blocat” prea mult timp în depanator. În aceste situații, poate fi necesar să se ajusteze semnalele blocate, fire de execuție controlate și, dacă este cazul, să se modifice configurația sistemului în sine (timpii de timeout, watchdog-uri hardware) pentru a permite sesiuni de depanare mai lungi.
Utilizarea eficientă a gdbserver implică combinarea mai multor elementeCompilează cu simboluri adecvate, alege gdb-ul corect pentru arhitectură, configurează căile către simboluri și surse, înțelegi cele două moduri principale ale gdbserver (single-process și multi-process) și nu te teme să extragi din modul --debug când conexiunea nu se comportă conform așteptărilor. Având această bază, depanarea aplicațiilor care rulează pe un sistem Linux la distanță, un router sau o mașină virtuală cu un kernel personalizat de pe PC devine destul de rutină și, mai presus de toate, incredibil de utilă.
Scriitor pasionat despre lumea octeților și a tehnologiei în general. Îmi place să îmi împărtășesc cunoștințele prin scriere și asta voi face în acest blog, să vă arăt toate cele mai interesante lucruri despre gadgeturi, software, hardware, tendințe tehnologice și multe altele. Scopul meu este să vă ajut să navigați în lumea digitală într-un mod simplu și distractiv.