gdb란?

  • gdb는 GNU에서 만든 디버거이다.
  • gdb는 C, C++, Objective-C, Java, Go, Rust 등의 많은 언어를 지원하며, arm, x86, IA-64 등 수 많은 아키텍쳐를 지원한다.

gdb를 이용한 간단한 디버깅

아래와 같은 코드가 있다고 가정하자.

// main.c
#include <stdio.h>

~

위 코드를 다음과 같은 명령어로 컴파일하자.

gcc -g -o main main.c

  • gdb를 이용해 컴파일하려면 -g 옵션을 붙여야 한다. 이는 컴파일 시 실행파일에 여러 디버깅 정보(심볼 문자열, 심볼 주소, 컴파일에 사용된 소스파일, 인스트럭션에 해당하는 코드 정보 등)를 삽입하는 옵션이다.
  • -g 옵션을 주고 컴파일 시 최적화 옵션(-O 등)을 사용하지 않는 것이 좋다. 이는 코드 및 어셈블리 코드에 변형을 가하기 때문에 디버깅이 힘들어질 수 있다.

이 상태로 ./main 명령어를 이용해 실행한다면 오류가 발생하는 것을 확인할 수 있다.

실행 및 종료

gdb를 이용해 프로그램을 load하는 방법은 아래와 같다.

  • gdb {program_name} : gdb main
  • gdb {program_name} {core_file_name} : gdb main core.1408
  • gdb {program_name} {실행중인 PID} : gdb main 1408

gdb를 종료하는 방법은 아래와 같다.

  • q
  • [Ctrl] + d

gdb를 이용해 프로그램을 load한 상태에서 프로그램을 실행시키는 방법은 다음과 같다.

  • r
  • r arg1 arg2 : arg1과 arg2를 프로그램 인자로 실행
  • k : 프로그램 실행 종료

현재 지점에서 라인 단위, 또는 다음 브레이크 포인트까지 진행하는 명령어는 다음과 같다.

명령어 설명
s (step) 현재 행 수행후 멈춤.
함수호출 지점에서는 함수 내부로 들어감  
s 6 s를 6번 수행한 것과 같음
n (next) 현재 행 수행후 멈춤
함수 호출 지점에서는 함수 내부로 들어가지 않고 수행 후 다음 행으로 감  
n 6 n을 6번 수행한 것과 같음
c (continue) 다음 브레이크 포인트를 만날 때 까지 수행
u (until) 현재 loop문에서 빠져나올 때 까지 수행
for  
finish 현재 함수를 수행하고 빠져나감
return 현재 함수를 수행하지 않고 빠져 나감
return 1 현재 함수를 수행하지 않고 빠져 나감
return 값으로 1을 줌  
advance 20 현재 파일의 20번 라인을 만날 때 까지 진행
advance file.c:20 file.c 파일의 20번 라인을 만날 때 까지 진행

소스 보기

gdb를 이용해 프로그램을 load한 뒤 소스 코드를 보는 방법은 다음과 같다.

명령어 설명
l main 함수를 기점으로 소스 출력
l 10 Line number 10 을 기준으로 출력
l func func 함수의 소스를 출력
l - 출력된 행의 이전 행을 출력
l file.c:func file.c파일의 func 함수 부분을 출력
l file.c:10 file.c의 Line number 10 부근을 출력
set listsize 20 l 명령의 기본 출력 코드수를 20라인으로 설정

브레이크 포인트

프로그램 수행 시 특정 지점 또는 특정 조건에서 break하고자 하는 경우가 있다. 이 때 다음 명령어를 사용하면 된다.

명령어 설명
b func func 심볼의 시작 부분(함수 내부 진입 전)에 브레이크 포인트 설정
b 10 10번 라인에 설정
b file.c:func file.c 파일의 func 심볼에 설정
b file.c:10 file.c 파일의 10번 라인에 설정
b +2 현재 라인에서 2개 라인 이후 지점에 설정
b -2 현재 라인에서 2개 라인 이전 지점에 설정
b *0x8040000 0x8040000 주소에 설정(어셈블리 디버깅 시 사용)
b 10 if myVar == 0 10번 라인에서 myVar의 값이 0 일때 브레이크
rb func* func*에 해당하는 모든 심볼에 설정
rb ^fun fun으로 시작하는 모든 심볼에 설정
rb TestClass:: TestClass에 해당하는 모든 심볼에 설정

브레이크 포인트를 설정하게 되면 브레이크 포인트마다 고유한 숫자(1부터 시작)가 부여된다. 이 때, 해당 브레이크 포인트에 추가적인 조건을 걸고 싶을 때 다음과 같은 명령어를 사용하면 된다.

  • condition {brakpoint_ID} myVar == 0
  • condition {brakpoint_ID} myFunc(0) > 0 : myFunc(0)의 결과값이 0보다 클 때 브레이크
  • ignore {brakpoint_ID} 100 : 해당 브레이크 포인트가 100번 지나갈 때 까지 무시. 이는 loop문에서 유용하다.

이러한 브레이크 포인트는 gdb가 종료될 때 까지 유효하다. 따라서 한 번만 사용하는 브레이크 포인트는 b 명령어 대신 tb 명령어를 사용하면 된다.

브레이크 포인트 제거

명령어 설명
cl func func 함수의 시작 부분에 브레이크 포인트 지움
cl 10 10번 라인의 브레이크 포인트 지움
d 모든 브레이크 포인트를 지움
disable br 모든 브레이크 포인트 비활성화
disable br 1 3 1번, 3번 브레이크 포인트 비활성화
enable br 모든 브레이크 포인트 활성화
enable br 1 1번 브레이크 포인트 활성화
enable br once 1 1번 브레이크 포인트를 활성화하고, 한 번만 걸리고 비활성화되게 설정
enable br delete 1 1번 브레이크 포인트를 활성화 하고, 한 번ㅁ나 걸리고 제거되게 설정

현재 설정되어있는 브레이크 포인트를 보려면 다음의 명령어를 사용하면 된다.

  • info breakpoints, info b

와치 포인트 설정

특정 변수의 값이 바뀔 때 마다 브레이크를 걸려면 와치 포인트를 사용한다. 이 때, 지역변수에 대한 설정은 해당 지역 변수의 스코프 내에서 설정하여야 한다.
이는 다음과 같이 사용할 수 있다.

  • watch {myVariable} : myVariable에 값이 쓰여질 때 마다 브레이크
  • rwatch {myVariable} : myVariable의 값이 읽혀질 때 마다 브레이크
  • awatch {myVariable} : myVariable에 값이 쓰여지거나 읽혀질 때 마다 브레이크

변수 및 레지스터의 값 검사

명령어 설명
info locals 현재 스코프에서 모든 지역변수의 값들을 확인
info variables 모든 전역변수의 값들을 확인
Non-debugging 심볼들도 포함되어 출력됨.  
info (all-)registers 모든 레지스터(MMX 레지스터 포함)의 값을 확인한다.
p myVariable myVariable의 값을 출력
p *ptr  
p **ptr 포인터 변수의 경우 주소값이 아닌 해당 주소에 저장되어있는 값을 확인
p *ptr@4 ptr이 가리키는 것이 4개 크기의 배열일 경우 4개의 값을 모두 출력
p $eax 특정 레지스터의 값을 확인
p 'file.c'::myVariable 특정 파일의 전역변수(myVariable)을 확인
p myFunc::myVariable 특정 함수의 static 변수의 값을 확인
p (char *)ptr 특정 형식으로 변환해서 값을 출력
void *ptr = "abc"; 와 같은 경우  
p myVariable = 10 myVariable의 값을 10으로 설정
p ptr + 4 특정 byte-offset 위치를 지정하여 출력
p/{출력 옵션} myVariable 여기서 출력 옵션에 들어갈 수 있는 값을 다음과 같다.
- t: 2진수로 출력  
- o: 8진수로 출력  
-d: 부호가 있는 10진수로 출력  
-u: 부호가 없는 10진수로 출력  
-x: 16진수로 출력  
-c: 최소 1바이트 값을 문자형으로 출력  
-f: 부동 소수점 형식으로 출력  
display myVariable 변수의 값을 매번 디스플레이한다.
n, s 명령어를 통해 라인 단위로 실행하며 값을 확인하고자 할 때 유용  
display/{출력 옵션} myVariable 출력 옵션에 맞추어 값을 출력
p 명령어의 출력옵션과 동일  
undisplay {display_ID} 디스플레이 설정을 없앤다
disable display {display_ID} 디스플레이를 비활성화 한다
enable display {display_ID} 디스플레이를 활성화 한다

스택 프레임의 정보를 이용해 디버깅

보통 프로그램을 실행하여 특정 부분에서 오류가 발생해 종료된 경우 스택이 깨지지 않은 이상 스택 프레임은 종료될 당시의 상태를 유지한다. 이 때, bt(backtrace)명령어를 통해 전체 스택프레임 정보를 확인할 수 있다. 이와 관련하여 스택프레임에 대한 자세한 명령어들은 다음과 같다.

명령어 설명
info frame 스택 프레임의 정보를 출력
frame {frame_number} 특정 스택 프레임으로 변경
up 상위 스택 프레임으로 이동
up N N번 상위 스택 프레임으로 이동
down 하위 스택 프레임으로 이동
info args 함수가 호출될 때 인자를 출력
info locals 함수의 지역 변수를 출력
info catch 함수의 예외 핸들러를 출력

 

기타 유용한 명령어

어셈블리 코드 보기

  • disas myFunc: func 함수의 어셈블리 코드를 보여준다.

call 명령을 통한 함수별 모듈 테스트

  • call myFunc(123): 현재 상태에서 임의로 함수를 출력한다. 이는 리턴값을 추가로 출력해준다.

jump 명령을 통한 임의의 행, 주소, 함수로 분기

  • jump *0x0804000: 해당 주소로 무조건 분기
  • jump 10: 10번 라인으로 분기
  • jump myFunc: myFunc 함수로 분기

프로세스에 시그널 보내기

  • signal SIGKILL: 현재 수행중인 프로그램에 signal을 보낸다.

메모리 특정 영역에 값을 설정

  • set {{type}}{address} = {value}:
    이는 set {int}0x804000 = 100와 같이 사용되며 특정 메모리 영역에 값을 설정하게 해 준다.
    또한 이는 p *0x804000=100와 같은 결과를 가져다 준다.

gdb 환경 설정 관련 명령

명령 설명
info set set 명령으로 설정가능한 명령어들의 리스트를 보여준다.
set print array on array를 출력할 때 여러 줄로 출력해 준다.

$2 = {0x804000 "one",
0x804004 "two",
0x804008 "three",
0x0}
info functions 함수들의 리스트를 출력
info types 선언된 타입들을 출력

+ Recent posts