Linux/Linux_technic

Linux :: 디버거 GDB 디버깅 테크닉

sosal 2010. 1. 31. 10:46
반응형

/*
 http://sosal.tistory.com/
 * made by so_Sal
 */

1. 디버깅 정보를 담아 컴파일 하기
2. gdb 실행하기, 프로그램 디버깅 시작하기, gdb 종료하기
3. 프로그램의 c언어 소스 보기 (디버깅 정보 포함)
4. 프로그램의 어셈블리 코드 보기
5. break 포인트. 디버깅 실행과 진행 명령어
6. 변수, 포인터 값 보기, 출력 형식 지정,함수 리턴값 지정 등 value 값 모니터링 하기
7. 변수값 바꾸기
8. 레지스터 값 보기
9. 메모리가 가지는 값 확인하기
10. 디버깅중인 프로세스에 시그널 보내기

gdb :: GNU Debugger

GDB는 컴퓨터 프로그램을 실행하여, 사용자에게 실행 과정을 추적하고,
중간에 임의로 수정, 심볼(함수,변수)들을 모니터링을 할 수 있도록 한다.




1. 디버깅 정보를 담아 컴파일 하기


gcc -g source.c -o program
gcc -g -o program source.c



디버그모드로 컴파일된 program과 옵션없이 컴파일된 program1 의 size를 보면
디버그 모드로 된 program 의 크기가 더욱 큰것을 볼 수 있다. (디버깅 정보가 삽입)
디버깅 정보에는 프로그램에서 사용하는 심볼정보 (변수, 함수, 문자열, 각각의 주소 등)와
컴파일에 사용된 소스, 그리고 컴파일된 인스트럭션들이 어떤 소스의 행에 해당하는 정보 등이 포함된다.

이 정보들을 이용하여 프로그램의 어셈블리 코드와 C언어를 동시에 보면서 디버깅 하는것이 가능하다.



2. gdb 실행하기, 프로그램 디버깅 시작하기, gdb 종료하기

gdb 실행하기
    gdb [프로그램명]                      //일반 파일 디버깅
    gdb [프로그램명] [프로세스 PID] //프로세스 디버깅



(gdb) run  argv[1] argv[2]...  // (또는 r argv[1].... )
gdb 상에서 프로그램이 실행됩니다. 프로그램이 지원하는 새로운 권한 (setuid 등)은 상속되지 않습니다.
실행 후 중간에 멈춘 후, 다시 run 명령을 했을 경우
프로그램을 새로 처음부터 시작합니다.


3. 프로그램의 c언어 소스 보기 (디버깅 정보 포함)

l (또는 list) :: 프로그램 소스의 정보가 출력된다. (-g 디버깅정보가 포함되어있어야 가능)
   default로 10줄의 소스를 보여준다.
   다시 l을 누르거나 Enter를 치면 다음 10줄의 소스를 보여준다.
   l [number] :: 입력된 수만큼의 소스를 출력한다.

   set listsize [number] 명령어를 통해 출력되는 행의 갯수를 조절할 수 있다.
   l [function] 지정된 함수의 소스를 출력한다.

   <여러개의 파일로 컴파일 된 경우>
   l [file.c]:[number]
   l [file.c]:[function]



4. 프로그램의 어셈블리 코드 보기


disassemble (disas, disass, disasse.... ):: 프로그램의 어셈블리 코드 보기
     disas [function] :: 함수 부분의 disassemble 한 코드들을 보여준다.
                   (예를 들면 disas main)
     disas [0xffffffa0] [0xffffffffa9] :: 주소 범위 사이의 어셈블리 코드를 보인다.



5. break 포인트. 디버깅 실행과 진행어

run (or r) 명령어로 실행 시키면
프로그램이 처음부터 끝날때까지 쭉~ 실행되기만 한다.
이래서는 중간중간의 심볼 현황을 살펴 볼 수 없다.
그래서 원하는 순간에 멈추기 위해 break (b) 명령어를 사용한다.

break (b) :: run 명령어가 실행된 후 멈출 메모리 위치
b [function]            // 함수 시작부분에 브레이크 포인트 설정
b [number]            // 어셈블리 코드에서 10행에 브레이크 포인트 설정

                // 아래는 프로그램의 소스가 여러 파일로 이루어졌을 경우
b [file.c]:[function]
b [file.c]:[number]

cl 명령어로 브레이크 포인트를 지울 수 있다.
d :: 모든 브레이크를 지운다.
브레이크 포인트를 건 다음, run 실행시키면 원하는곳까지 진행한다.


프로그램 진행 루틴 다루기

ni     :: 어셈블리 소스의 한줄을 실행한다. 함수가 호출되는 부분도 한줄로 인식한다.
si     :: 어셈블리 소스의 한줄을 실행하는데, 함수가 호출된다면 함수루틴으로 들어간다.

next (n) :: c언어 소스의 한줄을 실행한다. 함수 호출시, 한줄로 처리하여 함수를 수행한다.
step (s) :: c언어 소스의 한줄을 실행한다. 함수 호출시, 함수 루틴 안으로 들어간다.
(위 두 명령어는 디버깅 정보가 없다면 run이 된다.)

(ni, si, n, s, 명령어 뒤에 숫자를 붙일 시 그 수만큼 진행한다. ex: ni 5 ,   s 5)

c     :: 브레이크 포인트를 만날 때 까지 계속 진행한다.

finish  (f):: 함수가 끝난 지점으로 이동한다. (함수가 끝날 때 까지 모조리 실행)
return (r):: 함수가 끝난 지점으로 이동한다. (현재 함수를 실행하지 않는다.)
return 123 // 함수의 리턴값 123
until   (u):: 현재 루프를 빠져나간다.


6. 변수, 포인터 값 보기, 출력 형식 지정, 값 모니터링 하기


지역변수 / 전역변수 값 보기
info locals :: 현재 eip가 가리키고 있는 위치의 지역변수를 모두 출력한다.
info variables :: 현재상태에서의 전역변수 리스트를 확인할 수 있다.
p [value] :: (point의 약자) 변수 하나의 값을 보여줌

변수 이름이 중복 될 때,
p  'file name.c'::[value] // 전역변수
p   [function]::[value]   // 지역변수(함수)

출력 형식 지정
p/[format] $보고자하는것

p/t   //  2진수
p/o  //  8진수
p/d  // 10진수 (int)
p/u  // 부호없는 10진수 (unsigned int)
p/x  // 16진수 //주소를 보기위해 가장 많이 쓴다.
p/c  // 문자형 출력 (크기가 4byte 이상인 변수는 처음 1바이트를 출력한다.)
p/f   // 부동 소수점 값 형식으로 출력
p/a  // 가장 가까운 심볼의 오프셋을 출력
     (p/a 0x0801295 를 입력하면, 0x0801295와 가장 가까운 어셈블리 명령어줄의 offset을 출력)

void* buf = "hello world";
(gdb) p (char *)buf // 형변환도 가능하다.



포인트 변수 값 보기

p
명령어 (point) point 명령어는 변수 값, 함수의 주소값도 볼 수 있다.
(gdb) info locals
a = {_int = 10, _char = 37 '%', _double = -0.51}    //struct my_struct a
b = (struct my_struct *) 0x251ff4                        //struct my_struct *b

(gdb) p a                                              //구조체 a
$1 = {_int = 10, _char = 37 '%', _double = -0.51}

(gdb) p b
$2 = (struct my_struct *) 0x251ff4                           //포인터로 선언된 *b 구조체
(gdb) p *b
$3 = {_int = 1024, _char = 10 '\n', _double = 3.14 }   //포인터를 사용하여 값 확인
(gdb) p **b
Structure has no component named operator*.



변수 값 모니터링 하기
원하는 변수의 값이 바뀌는 지점에서
              브레이크를 걸려면 와치 포인트 명령어를 사용한다.

watch [value]
watch 명령어를 걸어놓은 후 c명령어로 실행시키면
value값이 바뀔 때 다시 멈춘다.

ex)
Old value = 1
New value = 2
main () at wz.c:7
7           i=3;
(gdb) c
Continuing.
Hardware watchpoint 2: i

Old value = 2
New value = 3
main () at wz.c:8
8           i=0;

display 화면에 변수값 자동으로 출력하기

display [변수명]
s, n, si, ni 등 eip를 진행할 때 마다 변수를 출력하도록 한다.
display/p, display/x 등 출력 옵션은 p(print)와 동일하다.
undisplay [display number]로 출력을 종료할 수 있다.

7.변수값 바꾸기

p 명령어로 변수의 값을 바꿀 수 있다.
p [value]=[바꿀 value];
예를들어, int a = 3이라면
(gdb)p a=10 명령어를 주면 a는 10으로 바뀐다.

8. 레지스터 값 보기

info register
(info reg)

(gdb) info reg
eax            0xbff23004       -1074647036
ecx            0xbff22f80       -1074647168
edx            0x1      1
ebx            0x639ff4 6529012
esp            0xbff22f44       0xbff22f44
ebp            0xbff22f68       0xbff22f68
esi            0x4f4ca0 5196960
edi            0x0      0
eip            0x8048365        0x8048365 <main+17>
eflags         0x286    [ PF SF IF ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51

info register $eax 로 원하는것 따로 볼 수 있다.

9. 메모리가 가지는 값 확인하기

x
명령어는 메모리 특정 범위의 값들을 확인하는데 사용하는 명령어다.
x/[범위][출력format][단위]
x/[범위][단위][출력format]

x/64bx $esp
:: 스택포인터의 시작지점부터 b(1바이트를) 64번 출력하는데, x(16진수)로 출력하라.

x/32cw main
:: 메인함수의 시작지점부터 w(4바이트를) 32번 출력하는데 c(문자열)로 출력하라.

(gdb) x/16bx $esp
0xbf94e3f0:     0x00    0x00    0x00    0x00    0x84    0x9a    0x04    0x08
0xbf94e3f8:     0x08    0xe4    0x94    0xbf    0x41    0x83    0x04    0x08


10. 디버깅중인 프로세스에 시그널 보내기

info signals 명령어로 보낼 수 있는 시그널의 종류들을 확인할 수 있습니다.

signal [SIGNAL]
 ex)   signal SIGALRM
         signal SIGKILL




참고자료 :: 유닉스 ,리눅스 프로그래밍 필수 유틸리티 / 한빛미디어