- gdbserver fungerer som en ekstern agent for GDB for å kontrollere prosesser på en annen maskin via TCP eller seriell tilkobling.
- For å feilsøke eksternt er det viktig å kompilere med symbolerBruk riktig gdb og konfigurer symbol- og skriftstier på riktig måte.
- gdbserver tilbyr enkeltprosessmodus og flerprosessmodus, og integrerer også med WinDbg og QEMU for kjernefeilsøking.
- Alternativer som --debug, sysroot og value size limits hjelper med å diagnostisere problemer og stabilisere økter.
Hvis du programmerer i C eller C++ Linux og du har aldri rørt gdbserverDu går glipp av et av de mest nyttige verktøyene for feilsøking av prosesser eksternt, enten det er på en server, et innebygd system eller til og med i en virtuell maskin eller WSL. Langt fra å være noe "magisk" eller reservert for eksperter, er gdbserver rett og slett et lite program som kommuniserer med gdb og kontrollerer utførelsen av målprosessen.
Hovedideen er veldig enkel.: på maskinen der binærfilen du vil feilsøke kjører (den mål) du starter gdbserver; på arbeidsdatamaskinen din (den vertDu starter gdb eller til og med WinDbg med støtte for gdb-protokollen. Begge kobles til via TCP eller en seriell port, og derfra kan du sette bruddpunkter, inspisere variabler, se stakken eller følge utførelsen trinn for trinn som om programmet kjørte på din egen maskin.
Hva er gdbserver, og når er det fornuftig å bruke det?

gdbserver er en ekstern feilsøkings-"agent" for GNU gdbFunksjonen er veldig spesifikk: den kjører på maskinen der programmet som skal analyseres kjører, kontrollerer den prosessen (eller prosessene) og kommuniserer med en gdb-klient som ligger på en annen maskin (eller på den samme) via en ekstern tilkobling.
I daglig bruk brukes gdbserver i to typiske scenarierFeilsøkingsprogramvare som kjører i innebygde miljøer (rutere, kort med nedstrippet Linux, enheter) IoTosv.) og feilsøkingsprosesser på eksterne Linux-maskiner, der det ikke er praktisk eller rett og slett ikke mulig å ha en "feit" gdb med alle bibliotekene og symbolene.
På et praktisk nivå håndterer gdbserver oppgaver som Les og skriv prosessregistre og minne, kontroller utførelse (fortsett, pause, gå gjennom), administrer avbruddspunkter og send alle disse dataene til gdb ved hjelp av GDB-fjernprotokollen. Denne filosofien er veldig lik den til verktøy som OpenOCD, som fungerer som en bro mellom gdb og en maskinvare ekstern, med den forskjellen at gdbserver kjører på samme system som binærfilen kjører.
Hvis du kommer fra miljøer Windows Det er også interessant å vite Feilsøkingsprogrammer som WinDbg kan kommunisere med en gdb-server på Linux, slik at du kan feilsøke brukerprosesser på Linux fra WinDbg ved å bruke støtten for ekstern feilsøking via gdb-protokollen som Microsoft har innlemmet i nyere versjoner.
Grunnleggende krav for feilsøking med gdb og gdbserver

Før du begynner å feilsøke eksternt, må du forstå forholdet mellom vert og mål.. Den mål Det er maskinen der programmet som skal feilsøkes kjører og der gdbserver vil bli utført; vert Dette er maskinen du skal kjøre gdb (eller WinDbg) fra, og hvor du vil ha kildekoden og helst feilsøkingssymbolene.
Det viktigste utgangspunktet er å kompilere binærfilen med symbolerI GCC eller g++ oppnås dette med flagget -gog det er vanligvis lurt å også deaktivere optimaliseringer (for eksempel med -O0Dette gjør at feilsøkingsprogrammet kan vise variabler, makroer og kodestruktur mer nøyaktig. For spesifikke makroer kan du bruke høyere feilsøkingsnivåer, for eksempel -g3.
På vertssiden trenger du en kompatibel versjon av gdb med målarkitekturen. For å feilsøke et MIPS-, ARM- eller annet innebygd system med arkitektur, må du bruke gdb-en til den tilsvarende kryssverktøykjeden (for eksempel) arm-none-eabi-gdb o gdb-multiarch) og, om nødvendig, konfigurer arkitekturen og endianness med kommandoer som set arch y set endian.
Når det gjelder tilkoblingen, støtter gdbserver to hovedtyperEn seriell kobling (svært vanlig i innebygd maskinvare, via UART) og TCP/IP, noe som er mest praktisk når målet er på samme nettverk eller er en Linux-maskin tilgjengelig over nettverket. I begge tilfeller brukes kommandoen fra gdb. target remote for å koble til endepunktet som er eksponert av gdbserver.
Måter å starte gdbserver på: enkeltprosess- og flerprosessmodus

gdbserver kan fungere på to hovedmåter Når vi snakker om feilsøking i brukermodus: direkte tilknyttet en enkelt prosess eller som en "prosessserver" som tillater oppføring og tilknytning til forskjellige systemprosesser.
I enkeltprosessmodus Du starter gdbserver, og spesifiserer host:port og programmet som skal kjøres. I et enkelt eksempel på en Linux-skrivebordsmaskin kan du gjøre noe slikt:
kommando: gdbserver localhost:3333 foo
Med den kommandoen starter gdbserver binærfilen. foo og han fortsetter å lytte på port 3333Inntil en ekstern gdb kobler seg til, forblir programmet stoppet; når gdb kobler seg til target remote localhost:3333, prosessen begynner å bli kontrollert av descrusheren.
I flerprosessmodus (prosessserver) brukes alternativet --multiI dette tilfellet starter ikke gdbserver noe program direkte, men lytter bare etter innkommende tilkoblinger og lar klienten (gdb eller WinDbg) administrere hvilken prosess som skal opprettes eller kobles til:
kommando: gdbserver --multi localhost:1234
Når man jobber med WinDbg på Linux, er denne multimodusen spesielt interessant.Fordi du fra WinDbg selv kan liste opp prosesser på det eksterne systemet, se PID, bruker og kommandolinje, og legge til den du er interessert i, på en lignende måte som det gjøres med prosessserveren. dbgsrv.exe på Windows.
Fjernfeilsøking med gdbserver og gdb trinn for trinn
La oss gjøre dette litt mer konkret med et veldig typisk eksempel.Feilsøk et enkelt program på samme maskin (vert og mål samsvarer) ved hjelp av gdbserver for å simulere det eksterne scenarioet.
Først skriver og kompilerer du et lite programFor eksempel, en tullete løkke som skriver ut en teller:
kommando: gcc -g foo.c -o foo
Nøkkelen her er flagget -gDette legger til nødvendig feilsøkingsinformasjon til binærfilen slik at gdb kan vise kodelinjer, variabelnavn, typer osv. I et "ekte" krysskompileringsmiljø ville du gjort denne kompileringen med kryssverktøykjeden og deretter kopiert både binærfilen og dens avhengigheter til målet.
Neste steg er å starte gdbserver på måletHvis vert og mål er den samme maskinen, så:
kommando: gdbserver localhost:3333 foo
Du vil se en melding som ligner på «Prosess foo opprettet; pid = XXXX; Lytter på port 3333». Dette indikerer at gdbserver har opprettet prosessen og venter på at gdb skal koble til. Hvis du er på et system der flere rettigheter kreves (for eksempel for å koble til systemprosesser), må du kanskje kjøre kommandoen med sudoMen det er alltid lurt å være forsiktig når man gir tillatelse. root til avsvovlingsenheten.
På verten starter du gdb ved å spesifisere den lokale kjørbare filen (den samme som kjører på målet, eller en identisk kopi med symboler):
kommando: gdb foo
Når du er inne i gdb, oppretter du den eksterne tilkoblingen med:
kommando: target remote localhost:3333
På det tidspunktet laster gdb symbolene fra den lokale binærfilen.Den synkroniserer med gdbserver og tar kontroll over prosessen som faktisk kjører under gdbserver. Derfra er flyten den vanlige: kommandoer som break å sette brytepunkter, continue, step, next, print å inspisere variabler, backtrace for å se batteriet osv.
Koble til kjørende prosesser med gdbserver
Du vil ikke alltid starte programmet fra bunnen av.Ofte er du interessert i å bli med i en prosess som allerede kjører (for eksempel en httpd en routeren systemdaemon eller en produksjonstjeneste).
Det typiske mønsteret er å bruke alternativet --attach fra gdbserversender porten der den skal lytte og PID-en til målprosessen. For eksempel, på en ruter der du har kopiert en gdbserver kompilert for arkitekturen, kan du gjøre:
kommando: gdbserver localhost:3333 --attach <pid_de_httpd>
På vertssiden bruker du en versjon av gdb som støtter ruterens arkitektur.for eksempel gdb-multiarchkonfigurere arkitekturen og endianness på forhånd:
kommando: set arch mips
set endian big
Deretter angir du den lokale filen som inneholder symbolene. av den eksterne binærfilen (for eksempel file httpd) og, om nødvendig, forteller du gdb hvor binærfilen faktisk kjører på målet med set remote exec-file /usr/bin/httpdTil slutt, akkurat som før, kobler du deg til:
kommando: target remote 192.168.0.1:3333
Når den er festetDu kan angi avbruddspunkter for bestemte funksjoner (for eksempel break checkFirmware), fortsett kjøringen og la programmets normale flyt (for eksempel opplasting av fastvare fra webgrensesnittet) utløse stoppunktet.
Bruke gdbserver med WinDbg på Linux
I de senere år har Microsoft lagt til støtte for feilsøking av Linux-prosesser i WinDbg. Bruker gdbserver som backend. Denne funksjonaliteten er ment for scenarier der du jobber i Windows, men koden kjører på Linux (inkludert WSL).
For å feilsøke en spesifikk Linux-prosess med WinDbg ved hjelp av gdbserverFlyten ville være omtrent slik: først finner du målprosessen på Linux-maskinen med en kommando som ps -A (for eksempel en python3 som kjører), så starter du gdbserver på målet:
kommando: gdbserver localhost:1234 python3
Hvis miljøet krever det, kan det hende du må bruke sudo gdbserver ...med de samme sikkerhetstiltakene som alltid. Når gdbserver indikerer at den «Lytter på port 1234», går du til «Fil / Koble til ekstern feilsøker» i WinDbg og angir en tilkoblingsstreng av følgende type:
kommando: gdb:server=localhost,port=1234
WinDbg bruker en liten gdb-protokoll-"driver" for å kommunisere med gdbserver og når forbindelsen er opprettet, forblir den stoppet på det punktet hvor boot av prosessen. Derfra kan du bruke stakkvinduene, modulene, minnet, avbruddspunktene, samt kommandoer som k for å se batteriet eller lm for å liste opp moduler (husk at noen kommandoer forventer PE-format og ikke ELF, så de kan vise merkelige data i visse tilfeller).
gdbserver og WinDbg-prosessserver
I tillegg til enkeltprosesstilfellet kan WinDbg koble til en gdbserver som fungerer som en prosesserver. å fungere mer likt hvordan det fungerer med eksterne Windows-prosesser. I denne modusen startes gdbserver med --multi og uten en tilknyttet prosess:
kommando: sudo gdbserver --multi localhost:1234
Fra WinDbg, velg «Fil / Koble til prosessserver» og du bruker tilkoblingsstrengen på nytt gdb:server=localhost,port=1234Når tilkoblingen er aktiv, kan du liste opp de tilgjengelige Linux-prosessene og legge dem til den du ønsker, eller til og med starte en ny prosess.
En subtil detalj må huskes.WinDbg skiller mellom «prosessserver» og «enkelt mål» avhengig av om gdbserver allerede er koblet til en prosess når den kobler seg til. Hvis du lot gdbserver være koblet til en prosess, lukket WinDbg og deretter prøvde å koble til på nytt, kan det hende at den ikke blir oppdaget som en prosessserver, og du må kanskje starte gdbserver på nytt.
For å avslutte en prosessserverøktVanligvis er det tilstrekkelig å bare trykke CTRL+D i konsollen der gdbserver kjører og stoppe feilsøking fra WinDbg. I noen ekstreme tilfeller, hvis det er synkroniseringsproblemer, kan det være nødvendig å lukke feilsøkingsprogrammet helt og starte gdbserver på nytt fra bunnen av.
Symbol- og kildekodehåndtering i ekstern feilsøking
En av nøklene til å gjøre ekstern feilsøking praktisk er å ha symbol- og skrifttypene godt løst.Uten symboler blir det tortur å navigere i stakken eller sette avbruddspunkter på spesifikke funksjoner.
I klassiske gdb + gdbserver-scenarier er det ideelt å beholde en kopi av den kjørbare filen med symboler på verten. (ikke strippet) og kildetreet. gdb krever ikke at den eksterne binærfilen inneholder symbolene; det er tilstrekkelig at den lokale filen du laster inn med file samsvar med den eksterne kjørbare filen på offset-nivå.
I WinDbg- og Linux-feilsøkingsverdenen har tjenester som DebugInfoD også dukket opp.som eksponerer symboler og fonter over HTTP. WinDbg kan bruke spesielle stier av typen DebugInfoD*https://debuginfod.elfutils.org begge i .sympath som i .srcpath for å laste ned DWARF-symboler og Linux ELF-binærkildekode på forespørsel.
I et spesifikt eksempel med WSL, hvor brukerkoden er under C:\Users\Bob\Du kan si til WinDbg:
kommando: .sympath C:\Users\Bob\
.srcpath C:\Users\Bob\
Og hvis du også vil bruke DebugInfoD for systembinærfiler:
kommando: .sympath+ DebugInfoD*https://debuginfod.elfutils.org
.srcpath+ DebugInfoD*https://debuginfod.elfutils.org
Med denne konfigurasjonen, når du inspiserer stakken eller skriver inn libc-funksjonerWinDbg kan forsøke å laste ned de tilsvarende DWARF-symbolene, og hvis serveren også eksponerer koden, vise kildekoden i betydelig detalj, selv om Windows-verktøykjeden internt ikke håndterer ELF og DWARF like "native" som PE og PDB.
Praktisk eksempel: feilsøking av et C++-program med gdbserver og WinDbg
Et illustrerende eksempel er et lite C++-program som skriver en hilsen til skjermen., kompilert i WSL med feilsøkingssymboler. Tenk deg et program som reserverer en std::array<wchar_t, 50> og kopierer en lengre melding inn i den, slik at teksten blir avkortet og tegn vises ???? på slutten
Etter å ha kompilert med noe sånt som:
kommando: g++ DisplayGreeting.cpp -g -o DisplayGreeting
Du starter gdbserver mot den binærfilen:
kommando: gdbserver localhost:1234 DisplayGreeting
I WinDbg kobler du til med strengen gdb:server=localhost,port=1234 Og når økten er opprettet og symbol- og skriftstier er konfigurert, angir du et avbruddspunkt i DisplayGreeting!maindu kan bruke dx greeting for å inspisere den lokale tabellen og se størrelsen (50 posisjoner), og visuelt sjekke i minnefanen eller i variabelvisningen hvordan hilsenen blir avbrutt.
Det fine med dette eksemplet er at det demonstrerer at selv uten full støtte for alle ELF/DWARF-formater i WinDbgDu kan navigere i stabler, inspisere typer, angi avbruddspunkter etter funksjonsnavn og navigere gjennom C++-kode rimelig komfortabelt ved å bruke gdbserver som en ekstern backend.
Feilsøking av Linux-kjernen med qemu og gdb
gdbserver brukes ikke bare i brukermodus; det finnes også svært kraftige scenarier i kjernemodus.spesielt når du kombinerer QEMU med feilsøkingsstøtte. Selv om rollen som «gdbserver» her oppfylles av QEMUs eget alternativ, er tilnærmingen identisk: den ene enden kjører systemet som skal feilsøkes og åpner en gdb-port; den andre enden er enten gdb eller en feilsøker som snakker den eksterne protokollen.
For å feilsøke kjernen må du kompilere den med spesifikke feilsøkingsalternativer.: aktivere generering av feilsøkingsinformasjon (CONFIG_DEBUG_INFO), GDB-kjerneskriptene (CONFIG_GDB_SCRIPTS) og kjernens egen feilsøkingsmodus (CONFIG_DEBUG_KERNELDet er også viktig å deaktivere alternativer som fjerner symboler under lenking, for eksempel «Fjern assemblergenererte symboler under lenking».
Etter kompilering vil du få en binærfil vmlinux «ikke strippet»som er den du skal bruke fra gdb. Du trenger også en grunnleggende initramfs, som du kan generere med en kommando som:
kommando: mkinitramfs -o ramdisk.img
Så starter du QEMU med feilsøkingsparametereEt typisk eksempel inkluderer alternativet -gdb tcp::1234 for å åpne et gdb-kompatibelt eksternt endepunkt, og -S slik at den virtuelle maskinen starter pauset fra begynnelsen. Du spesifiserer også kjernen med -kernel vmlinux, The -initrd ramdisk.img, minnet med -m 512 og du omdirigerer vanligvis konsollen til ttyS0 å administrere alt fra terminal.
Med QEMU arrestert i påvente av gdbFra vertsmaskinen starter du gdb som peker til vmlinux og du kobler deg til target remote localhost:1234Derfra kan du sette tidlige bruddpunkter, for eksempel en hb start_kernelog kontrollere utførelsen med kommandoer som c (fortsett) og CTRL+C for å sette på pause igjen.
Nylige endringer og nyanser i gdb og gdbserver
I moderne distribusjoner som Red Hat Enterprise Linux 8 er det en rekke endringer i gdb og gdbserver som er verdt å huske på.spesielt hvis du kommer fra tidligere versjoner eller har skript som analyserer feilsøkingsutdataene.
På den ene siden starter gdbserver nå de «nedre» prosessene ved hjelp av et skallAkkurat som med gdb, tillater dette variabelutvidelse og -substitusjoner på kommandolinjen. Hvis du av en eller annen grunn trenger å deaktivere denne oppførselen, finnes det spesifikke innstillinger dokumentert i RHEL 8 for å gå tilbake til forrige modus.
Flere ting har også blitt fjernet eller endretfeilsøkingsstøtte for Java-programmer kompilert med gcj, HP-UX XDB-kompatibilitetsmodusen, kommandoer som set remotebaud (erstattet av set serial baud) eller kompatibilitet med et visst eldre format av stabsVidere er trådnummereringen ikke lenger global, men etter "nedre" tråd, og vises som inferior_num.thread_num, med nye bekvemmelighetsvariabler som $_gthread å referere til den globale identifikatoren.
En annen relevant ny funksjon er justeringen max-value-sizeDette begrenser mengden minne som gdb kan allokere for å vise innholdet i en verdi. Standardverdien er 64 KiB, så forsøk på å skrive ut store matriser eller massive strukturer kan resultere i en advarsel om at verdien er for stor i stedet for å vise alt tilgjengelig minne.
Det har også blitt justert hvordan gdb håndterer sysrootStandardverdien er nå target:Dette betyr at for eksterne prosesser vil den først forsøke å finne biblioteker og symboler på målsystemet. Hvis du vil at den skal prioritere lokale symboler, bør du kjøre set sysroot med ruten som interesserer deg før du gjør det target remote.
Når det gjelder kommandohistorikken, er miljøvariabelen som nå brukes GDBHISTSIZE i stedet for HISTSIZEDette lar deg finjustere hvor lenge du vil beholde kommandoene du har skrevet i feilsøkingsøkter uten å forstyrre virkemåten til andre applikasjoner som bruker linjelesingsbiblioteket.
Tips til arbeidsflyt og feilsøking med gdbserver
For å ha en komfortabel arbeidsflyt, finnes det noen mønstre som pleier å fungere veldig bra. Når man utvikler for innebygde systemer eller eksterne servere, er det første trinnet å automatisere symbolkompilering og binærdistribusjon til målet så mye som mulig. På denne måten vet man alltid hvilken versjon av den kjørbare filen som kjører, og man har symbolkopien lett tilgjengelig på verten.
I miljøer med mange krasjkjerner er det verdt å lære hvordan man bruker gdb i batchmodus., med flagg som --batch, --ex y -x å automatisk starte kommandoer på en liste over kjerner og behandle tilbakesporingene deres fra skript (for eksempel i PythonDette lar deg raskt filtrere ut gjentatte problemer, gruppere feil etter stakkspor osv.
Når noe går galt med den eksterne tilkoblingen, vises alternativet --debug gdbserver er din beste vennHvis du for eksempel starter en prosesserver med:
kommando: gdbserver --debug --multi localhost:1234
gdbserver-konsollen vil vise detaljerte spor av hva som skjer På eksternt protokollnivå inkluderer dette innkommende pakker, formateringsfeil, frakoblingsproblemer osv. Dette er veldig nyttig når gdb-serveren din plutselig kobler fra, en prosess krasjer så snart et avbruddspunkt er satt, eller feilsøkingsgrensesnittet ditt sender noe som gdbserver ikke forstår.
I sammenhenger som en TP-Link-ruter der du kobler gdbserver til en kritisk prosess som httpdDet er relativt vanlig at visse bruddpunkter skaper kappløpsbetingelser eller watchdogs som dreper prosessen når den forblir "fast" for lenge i feilsøkingsprogrammet. I disse situasjonene kan det være nødvendig å justere hvilke signaler som blokkeres, hvilke tråder som kontrolleres, og hvis aktuelt, endre selve systemkonfigurasjonen (timeout-tider, maskinvare-watchdogs) for å tillate lengre feilsøkingsøkter.
God bruk av gdbserver innebærer å kombinere flere delerKompiler med passende symboler, velg riktig gdb for arkitekturen, konfigurer symbol- og kildestier, forstå de to hovedmodusene til gdbserver (enkeltprosess og flerprosess), og ikke vær redd for å trekke fra modusen. --debug når tilkoblingen ikke oppfører seg som forventet. Med det grunnlaget blir feilsøking av applikasjoner som kjører på et eksternt Linux-system, en ruter eller en virtuell maskin med en tilpasset kjerne fra PC-en din ganske rutinemessig og fremfor alt utrolig nyttig.
Lidenskapelig forfatter om verden av bytes og teknologi generelt. Jeg elsker å dele kunnskapen min gjennom å skrive, og det er det jeg skal gjøre i denne bloggen, vise deg alle de mest interessante tingene om dingser, programvare, maskinvare, teknologiske trender og mer. Målet mitt er å hjelpe deg med å navigere i den digitale verden på en enkel og underholdende måte.