소프트웨어 개발에서 디버깅은 필수적인 과정이다. 특히 시스템 프로그래밍이나 저수준 개발을 할 때는 강력한 디버깅 도구가 필요하다. GNU Debugger(GDB)는 C, C++, 그리고 어셈블리 수준의 디버깅을 지원하는 가장 강력한 도구 중 하나다. 이번 글에서는 GDB의 기본 개념부터 실전 활용까지 다룬다.
1. GDB란 무엇인가?
GDB(GNU Debugger)는 프로그램 실행을 제어하며, 오류를 분석하고 문제를 해결하는 데 사용되는 디버거다. 주요 기능은 다음과 같다.
• 브레이크포인트 설정: 특정 지점에서 실행을 멈추고 상태를 확인
• 단계별 실행(Step-by-step execution): 코드 한 줄씩 실행하며 흐름을 분석
• 메모리 및 변수 검사: 변수 값, 레지스터, 스택 상태 확인
• 어셈블리 코드 디버깅: 저수준에서 명령어 단위로 코드 실행 추적
• 코어 덤프 분석: 비정상 종료된 프로그램의 상태를 확인
2. GDB 설치 및 실행
GDB는 대부분의 리눅스 배포판에 기본 제공되지만, 설치되지 않았다면 아래 명령어로 설치할 수 있다.
# Ubuntu/Debian 계열
sudo apt install gdb
# Fedora
sudo dnf install gdb
# Arch Linux
sudo pacman -S gdb
GDB를 실행하는 방법은 간단하다. 디버깅할 바이너리 파일을 인자로 주면 된다.
gdb ./my_program
디버깅을 원활하게 수행하려면 반드시 디버깅 심볼(debug symbols) 이 포함된 상태로 컴파일해야 한다. 이를 위해 -g 옵션을 추가한다.
gcc -g -o my_program my_program.c
3. GDB 기본 명령어
GDB는 다양한 명령어를 제공하지만, 디버깅에서 가장 많이 쓰이는 기본 명령어는 다음과 같다.

4. 실전 예제: 코드 디버깅
간단한 C 프로그램을 작성한 후 GDB로 디버깅해보자.
4.1 디버깅할 프로그램
아래와 같은 debug_test.c 파일을 만든다.
#include <stdio.h>
void my_function(int x) {
int result = 10 / x; // x가 0이면 오류 발생
printf("Result: %d\n", result);
}
int main() {
int a = 0;
my_function(a);
return 0;
}
컴파일할 때 -g 옵션을 추가하여 디버깅 심볼을 포함시킨다.
gcc -g -o debug_test debug_test.c
4.2 GDB로 디버깅 시작
GDB를 실행하고 프로그램을 로드한다.
gdb ./debug_test
이제 브레이크포인트를 설정해보자. buggy_function 함수에서 오류가 발생할 것이므로, 해당 함수에 브레이크포인트를 설정한다.
(gdb) break my_function
Breakpoint 1 at 0x1149: file debug_test.c, line 5.
프로그램을 실행한다.
(gdb) run
Starting program: ./debug_test
이제 buggy_function에서 실행이 멈춘다. 변수 x의 값을 확인해보자.
(gdb) print x
$1 = 0
변수 x가 0이므로 10 / x 연산에서 오류가 발생할 것이다.
코드를 한 줄 실행하려면 next 또는 step 명령어를 사용한다.
(gdb) next
오류가 발생하면 GDB는 SIGFPE(0으로 나누기 오류)를 감지하고 메시지를 출력한다.
Program received signal SIGFPE, Arithmetic exception.
백트레이스를 출력하면 어디에서 오류가 발생했는지 확인할 수 있다.
(gdb) bt
#0 my_function (x=0) at debug_test.c:5
#1 0x0000000000401145 in main () at debug_test.c:10
이제 원인을 파악했으니, buggy_function에서 x가 0일 때 예외 처리를 추가하여 수정할 수 있다.
5. 어셈블리 코드 분석
GDB는 어셈블리 코드 단위의 디버깅도 지원한다. 특정 함수의 어셈블리 코드를 확인하려면 disassemble 명령어를 사용한다.
(gdb) disassemble my_function
Dump of assembler code for function my_function:
0x0000000000401136 <+0>: push %rbp
0x0000000000401137 <+1>: mov %rsp,%rbp
0x000000000040113a <+4>: mov %edi,-0x4(%rbp)
0x000000000040113d <+7>: mov -0x4(%rbp),%eax
0x0000000000401140 <+10>: cdq
0x0000000000401141 <+11>: idiv %ecx
...
이 정보를 활용하면 특정 레지스터 값과 함께 프로그램 흐름을 더 정밀하게 분석할 수 있다.
6. 마치며
GDB는 단순한 소스 코드 디버깅뿐만 아니라 어셈블리 수준의 분석도 지원하는 강력한 도구다. 특히 커널, 드라이버, 임베디드 시스템 개발자라면 필수적으로 익혀야 할 디버거다. GDB를 자유자재로 활용하면 디버깅 속도가 빨라지고, 문제를 더 깊이 이해할 수 있다.
'임베디드' 카테고리의 다른 글
Flash 제어 및 동작 방식 (0) | 2025.04.17 |
---|---|
Trace32 디버깅에 대해서 (0) | 2025.03.23 |
Endianess란 무엇일까? Big-Endian 과 Little-Endian (0) | 2025.03.21 |
ARM Exception Level(EL) 알아보기(2): EL2(하이퍼바이저)와 EL3(보안 모드) (0) | 2025.03.19 |
ARM Exception Level(EL) 알아보기(1): EL의 개념과 EL0~EL1 분석 (0) | 2025.03.18 |