GDB 사용법

GDB(GNU Debugger)는 프로그램을 디버깅하고 분석하는데 사용되는 강력한 도구이다. 이 글에서는 GDB의 기본적인 사용법에 대해 알아본다.

GDB 설치

GDB를 사용하기 위해선 먼저 설치해야 합니다. 다음은 Ubuntu 기반 시스템에서 GDB를 설치하는 명령어이다.

sudo apt-get install gdb

컴파일 옵션

gdb로 디버깅할 바이너리에 디버깅 정보를 포함하기 위해 -g 옵션주고 컴파일 한다.

/* hello.c */
#include <stdio.h>

int main(int argc, const char *argv[])
{
    printf("Hello World!!\n");
    return 0;
}
gcc -g -o hello hello.c

GDB 시작하기

gdb를 실행하는 방법은 gdb 실행시 디버깅 하려는 바이너리를 함께 입력하면 된다.

$ gdb hello
GNU gdb (Debian 13.1-3) 13.1
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
... ...
(gdb)

또는, gdb를 먼저 실행 후 file 로 바이너리를 부른다.

$ gdb
...
(gdb) file hello
Reading symbols from hello...
(gdb)

바이너리 실행

  • run(r) : 프로세스를 새로 실행한다. 이미 실행 중이라면 재시작한다.
  • continue(c) : 마지막 중단 지점에서 다음 중단점까지 프로세스를 재개한다.
  • next(n) : 실행 중인 프로세스를 한줄 실행한다. 함수 실행 시 내부로 진입 X
  • step(s): 실행 중인 프로세스를 한줄 실행한다. 함수 실행시 내부로 진입 O
  • finish(fin) : 현재 함수를 수행하고 빠져나간 후 리턴 깞을 출력한다.
(gdb) run
Starting program: /home/raspi/work/gdb/hello
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/aarch64-linux-gnu/libthread_db.so.1".
Hello World!!
[Inferior 1 (process 2220) exited normally]
(gdb)

실행 시 에러 발생

아래 코드는 메모리가 할당되지 않은 포인터에 strcpy를 하여 에러가 발생한다.

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
    char *ptr;
    strcpy(ptr, "abcd");
    return 0;
}

아래와 같이 segv.c:7 strcpy 함수에서 SIGSEGV(Segmentation Fault) 에러가 발생한다.

$ gcc -g segv.c
$ gdb a.out
...
(gdb) r
Starting program: /home/winuser/work/a.out
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Program received signal SIGSEGV, Segmentation fault.
0x000055555555513c in main (argc=1, argv=0x7fffffffe2e8) at segv.c:7
7           strcpy(ptr, "abcd");
(gdb)

breakpoint 설정

프로그램 실행 도중 특정 위치에서 실행을 멈추고자 할 때 breakpoint를 설정한다.

break(b) 설정

break(b)는 breakpoint를 걸 지점을 정할 때 사용하는데, 설정하는 방법은 다음과 같다.

  • break <함수이름>
  • break <라인번호>
  • break <파일이름:라인번호>
  • break <파일이름:함수이름>
  • break +<offset>
  • break -<offset>
  • break *address
  • break <…> if <condition> : 조건부 break

info(i) break(b)

break 지정 내역을 확인할 때 사용한다.

info break
info b
i b

clear

지정한 breakpoint를 지운다.

  • clear <설정된 함수이름>
  • clear <설정된 라인번호>
  • clear <설정된 파일이름:라인번호>
  • clear <설정된 파일이름:함수이름>

delete(d)

breakpoint를 지운다.

  • delete : 설정된 모든 breakpoint를 지운다.
  • delete <breakpoint 번호>
  • delete <breakpoint 번호> <breakpoint 번호> // 번호에 해당하는 breakpoint를 모두 지운다.

disable(dis) / enable(en)

breakpoint을 활성화/비활성화 한다.

enable(en)모든 breakpoint 활성화
enable <breakpoint number>해당 breakpoint 활성화
enable <breakpoint number> <breakpoint number>해당 breakpoint 모두 활성화
disable(dis)모든 중단점 활성화
disable <breakpoint number> 해당 중단점 비활성화
disable <breakpoint number1> <breakpoint number2>해당 중단점 모두 비활성화

<ctrl + c> 프로세스가 실행 중 중단점에 도달하지 않을 때 사용하여 일시 중단 가능하다.

Callstack 확인

backtrace

현재 위치의 callstack를 출력한다.

backtrace(bt)현재 실행 위치의 함수 call stack을 출력한다.
bt N현재 실행 위치의 함수 call stack 중 처음 N개 출력
bt -N현재 실행 위치의 함수 call stack 중 마지막 N개 출력
bt fulllocal 변수들도 함께 출력

값 출력 / 할당

print

변수나 주소 등을 출력한다.

print(p) <val>변수 출력
p <func::val>해당 함수의 변수를 출력한다.
p *<ptr>포인터의 값
p <addr>주소에 있는 값
p arr[n]arr 배열의 n번째 값 출력
p -pretty *<구조체>예쁘게 출력한다. 구조체를 출력할 때 용이하다.
p/x <val>x(16진수) 형식으로 변수 출력

display

매 실행(step, next, continue 등) 마다 출력한다.

display expr매 실행시 마다 출력한다.
display/fmt expr매 실행시 마다 fmt 포멧 형식으로 출력한다.
info display(dis)설정된 display를 출력한다.
enable(en) display(dis)모든 display를 활성화 시킨다.
en dis <번호> <번호>번호에 해당하는 display를 활성화 시킨다.
disable(dis) display(dis)모든 display를 비활성화 시킨다.
dis dis <번호> <번호>번호에 해당하는 display를 비활성화 시킨다.

set

변수/주소 등에 값을 할당한다.

set var <variable_name>=<value>변수값을 주어진 값으로 변경한다.

기타

return

현재 함수를 수행하지 않고 빠져나간다.

return현재 함수를 수행하지 않고 빠져나간다.
return -1현재 함수를 수행하지 않고 빠져나간다. 리턴값은 -1

list(l)

소스 파일을 출력한다.

list현재 위치의 소스 출력
list 100100번째 라인 주변 소스 출력
list funtion지정 함수의 소스 출력
list –직전에 출력한 소스 출력
set listsize count출력하는 라인수를 count로 설정한다.
set listsize unlimited출력하는 라인수 무제한

참고

답글 남기기