내밥줄/프로그래밍

[펌]gdb & gdb server cross compile for arm

jjoell 2009. 4. 23. 18:48

native gdb

localhost opt  # wget ftp://ftp.jaist.ac.jp/pub/GNU/termcap/termcap-1.3.1.tar.gz
localhost opt  # tar xvzf termcap-1.3.1.tar.gz
localhost opt  # cd termcap-1.3.1
localhost termcap-1.3.1# CC=arm-linux-gcc ./configure --build=i686-linux --host=arm-linux --prefix=$PWD/build
localhost termcap-1.3.1# make && make install
localhost termcap-1.3.1# cd ..

localhost opt  # wget ftp://ftp.jaist.ac.jp/pub/GNU/gdb/gdb-6.6.tar.gz
localhost opt  # tar xvzf gdb-6.6.tar.gz
localhost opt  # cd gdb-6.6
localhost gdb-6.6 # CFLAGS=-L/opt/termcap-1.3.1 ./configure --build=i686-linux --host=arm-linux --target=arm-linux --prefix=/nfsroot/gdb
localhost gdb-6.6 # make && make install


확인 필요
ptrace: bogus breakpoint trap 메세지가 너무 많이 나옴




gdb & gdbserver

localhost opt # wget http://core.ring.gr.jp/pub/GNU/gdb/gdb-6.6.tar.gz
localhost opt # tar xvzf gdb-6.6.tar.gz
localhost opt # cd gdb-6.6
localhost gdb-6.6 # ./configure --build=i686-linux --host=i686-linux --target=arm-linux
--prefix=$PWD/build
localhost gdb-6.6 #
make && make install
localhost gdb-6.6 # cp build/bin/arm-linux-gdb  /nfsroot/


localhost gdb-6.6 #
cd gdb/gdbserver
localhost gdbserver # CC=arm-linux-gcc ./configure --build=i686-linux --host=arm-linux --target=arm-linux
localhost gdbserver # cp gdbserver /nfsroot/




테스트

테스트 할 프로그램을 ARM용으로 컴파일 한다. 디버깅 옵션( -g )가 빠지지 않도록 주의해야 한다.
localhost nfsroot # arm-linux-gcc -g gdb_test.c


테스트를 하기 전에 컴파일결과 만들어진 파일들을 확인해보자.
localhost nfsroot # file a.out
a.out: ELF 32-bit LSB executable, ARM, version 1, for GNU/Linux 2.0.0, dynamically linked (uses shared libs), not stripped

localhost nfsroot # file arm-linux-gdb
arm-linux-gdb: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.9, dynamically linked (uses shared libs), not stripped

localhost nfsroot # file gdbserver
gdbserver: ELF 32-bit LSB executable, ARM, version 1, for GNU/Linux 2.0.0, dynamically linked (uses shared libs), not stripped


타겟보드에서 nfs로 pc에 마운트하여 테스트를 진행한다. 주의할 점은 PC와 타겟 모두 동일한 실행파일(a.out)과 소스코드(gdb_test.c)를 가지고 있어야 한다.

우선 minicom을 실행시키다.
localhost nfsroot # minicom


타겟보드에서 pc에  nfs 마운트한다. 타겟의 IP는 192.168.1.166, host pc의 IP는 192.168.1.167로 설정하였다.
# ifconfig eth0 192.168.1.166                                                    
# route add default gw 192.168.1.1                                               
# mount -t nfs 192.168.1.167:/nfsroot /nfsroot -o nolock,rsize=1024,wsize=1024


타겟보드에서 컴파일한 실행파일을 실행 시켜 보자.  허용되지 않은 메모리를 접근하려고 해서 segmentation fault 에러가 난 것을 확인 할 수 있다.
# cd /nfsroot
# ./a.out
debug test
pc : [<4007a174>]    lr : [<40082994>]    Not tainted
sp : bffffdb0  ip : 4012b068  fp : 0000000a
r10: 4012cce0  r9 : 00000000  r8 : 4012b01c
r7 : 000003ff  r6 : 00000400  r5 : 00000000  r4 : 00000000
r3 : 40015001  r2 : 00000064  r1 : 40015000  r0 : 00000064
Flags: nzCv  IRQs on  FIQs on  Mode USER_32  Segment user
Control: 397F  Table: A7CB8000  DAC: 00000015
Segmentation fault


타겟보드에서 자신의 IP와 임의의 포트번호,  디버깅할 실행파일 이름을 인자로 하여 gdbserver를 실행한다.
#./gdbserver 192.168.1.166:10000 a.out
Process a.out created; pid = 61                                                 
Listening on port 10000       


PC에서 디버깅할 실행파일을 인자로 하여 gdb를 실행한다.
localhost nfsroot # pwd
/nfsroot
localhost nfsroot # ./arm-linux-gdb  a.out
GNU gdb 6.6
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "--host=i686-linux --target=arm-linux"...
(gdb)

우선 디버거에서 사용할 라이브러리 위치를 알려주기 위해서 툴체인의 라이브러리 상위폴더를 지정해주어야 한다.
 (gdb) set solib-absolute-prefix /usr/local/arm-linux

pc에서 타겟보드로 연결 하자.
(gdb) target remote 192.168.1.166:10000
Remote debugging using 192.168.1.166:10000
0x40001c80 in _start () at rtld.c:-1
-1      rtld.c: No such file or directory.
        in rtld.c

타겟보드에서 다음 메세지를 확인 할 수 있다.
Remote debugging from host 192.168.1.167

우선 실행을 시켜보자. PC에서 continue 또는 c를 입력하자
(gdb) continue
Continuing.

그리고 나서 보드에서  test라고 입력한 후 엔터를 친다.
test
pc : [<4007a174>]    lr : [<40082994>]    Not tainted
sp : bffffdb0  ip : 4012b068  fp : 0000000a
r10: 4012cce0  r9 : 00000000  r8 : 4012b01c
r7 : 000003ff  r6 : 00000400  r5 : 00000000  r4 : 00000000
r3 : 40015001  r2 : 00000074  r1 : 40015000  r0 : 00000074
Flags: nzCv  IRQs on  FIQs on  Mode USER_32  Segment user
Control: 397F  Table: A7AC0000  DAC: 00000015

PC를 확인 해보면 Segmentation fault가 난 것을 알 수 있다. 잘못된 메모리 주소를 접근했기 때문에 난 에러이다.
Program received signal SIGSEGV, Segmentation fault.
0x4007a174 in ?? ()

gdb 명령 backtrace를 사용해보자. main함수 소스코드 라인 10줄에서 fgets함수를 호출 한 후, getline_info에서 실행이 멈춘것을 알 수 있다.  fgets의 인자로 받은 buf주소가 0x0=NULL 이어서 문제가 생긴 것이다.
(gdb) backtrace
#0  _IO_getline_info (fp=0x4012b01c, buf=0x0, n=1023, delim=10, extract_delim=1, eof=0x0) at iogetline.c:87
#1  0x4007a05c in _IO_getline (fp=0x74, buf=0x40015000 "test\n", n=116, delim=1073827841, extract_delim=1) at iogetline.c:41
#2  0x40079274 in _IO_fgets (buf=0x0, n=1024, fp=0x4012b01c) at iofgets.c:50
#3  0x00008478 in main (argc=1, argv=0xbffffe64) at debug_test.c:10


소스코드를  확인 해 보면 10번째 라인에 fgets함수를 볼 수 있다. 인자로 buf 를 사용하고 있다
(gdb) list debug_test.c:1
1       #include <stdio.h>
2       #include <stdlib.h>
3
4       int main( int argc, char **argv )
5       {
6               char *buf;
7
8               buf = malloc( 1<<31 );
9
10              fgets( buf, 1024, stdin );
(gdb) list +
11              printf( "%s\n", buf );
12
13              return 1;
14      }
(gdb)

스택 프레임을 main함수로 바꾸자
(gdb) frame 3
#3  0x00008478 in main (argc=1, argv=0xbffffe64) at debug_test.c:10
10              fgets( buf, 1024, stdin );

현재 스택 프레임에 대한 정보를 자세히 보려면
(gdb) info frame
Stack level 3, frame at 0xbffffe38:
 pc = 0x8478 in main (debug_test.c:10); saved pc 0x400381e0
 caller of frame at 0xbffffe1c
 source language c.
 Arglist at 0xbffffe34, args: argc=1, argv=0xbffffe64
 Locals at 0xbffffe34, Previous frame's sp at 0xbffffe2c
 Saved registers:
  r11 at 0xbffffe28, lr at 0xbffffe30, pc at 0xbffffe30

지역번수의 내용을 확인하면 buf의 주소가 NULL인 것을 볼 수 있다.
(gdb) info locals
buf = 0x0

아규먼트는 메인함수 호출시 사용된 것이 나온다.
(gdb) info args
argc = 1
argv = (char **) 0xbffffe64

print명령을 사용하면 포인터 변수인 buf의 내용과 가리키는 곳의 내용을 출력할 수 있다. 역시 여기서도 가리키는 곳을 접근 할수 없다는 메세지가 나온다.
(gdb) print buf
$1 = 0x0
(gdb) print *buf
Cannot access memory at address 0x0

현재 디버그 중인 프로그램을 중지 시키려면 kill명령을 사용하면 된다.
타겟보드에서 실행중인 gdbserver를 중지시키게 된다.
(gdb) kill
Kill the program being debugged? (y or n) y

타겟보드에서 디버깅이 종료된 것을 확인 할 수 있다.
Killing inferior                                                               
#




디버깅을 다시 시작 하기 위해 타겟보드에서 gdbserver를 다시 실행시킨다.
process id가 실행 할 때 마다 변하는 것을 볼 수 있다.
# ./gdbserver 192.168.1.166:10000 a.out
Process a.out created; pid = 69
Listening on port 10000

PC에서도 다시 gdbserver에 접속한다.
(gdb) target remote 192.168.1.166:10000
Remote debugging using 192.168.1.166:10000
0x40001c80 in _start () at rtld.c:-1
-1      rtld.c: No such file or directory.
        in rtld.c


breakpoint를 8번째 라인으로 설정한다.
(gdb) list debug_test.c:1
1       #include <stdio.h>
2       #include <stdlib.h>
3
4       int main( int argc, char **argv )
5       {
6               char *buf;
7
8               buf = malloc( 1<<31 );
9
10              fgets( buf, 1024, stdin );
(gdb) list +
11              printf( "%s\n", buf );
12
13              return 1;
14      }
(gdb) break 8
Breakpoint 1 at 0x8454: file debug_test.c, line 8.


(gdb) c
Continuing.

Breakpoint 1, main (argc=1, argv=0xbffffe64) at debug_test.c:8
8               buf = malloc( 1<<31 );

(gdb) print buf
$1 = 0x0
(gdb) print *buf
Cannot access memory at address 0x0


(gdb) n
10              fgets( buf, 1024, stdin );
(gdb) print buf
$2 = 0x0
(gdb) print *buf
Cannot access memory at address 0x0

출처:http://webnautes.tistory.com/entry/gdb-gdb-server-cross-compile-for-arm