이기종간의 환경에서 GDB 를 이용해서 디버깅하는 방법에 대해 설명하고 있다.
여기서 이기종이라는 것는 타겟(ARM)과 호스트(X86)가 다른 아키텍처일 경우를 뜻한다. 하지만, 여기서 다루는 내용은 타겟과 호스트가 같은 경우에도 적용이 가능하다. 실제로 타겟(X86)과 호스트(X86) 인 경우에도 잘 동작했었다.
준비운동하기
본격적인 시작에 앞서, 여기서는 X86 의 호스트에 Xscale 타겟을 이용할 것이다. 환경은 다음과 같다.
호스트 | 타겟(휴인스 PXA255-PRO3) | |
아키텍처 | X86 | Xscale |
OS | Redhat 9 | Linux |
커널 | 2.4.18 | 2.4.19 |
GDB | 6.5 | 6.5 |
IP 주소 | 192.168.0.1 | 192.168.0.2 |
ETC | 크로스케이블(LAN) | 툴체인(휴인스제공) |
표에서 보듯이 가장 중요한 GDB 의 경우, 2006년 11월 9일 현재 가장 최신버전을 사용했다.
http://www.gnu.org/software/gdb/download/ 에서 다운로드 할 수 있다.
호스트 설정
먼저 호스트쪽 부터 보도록 한다.
#tar xzf gdb-6.5.tar.gz #cd gdb-6.5 #./configure -target=arm-linux --prefix=/usr/local/arm-dev -v #make #make install
GDB 의 경우, 컴파일시에 현재 부팅한 커널의 소스를 참조한다. 그래서 만일 /usr/src 아래에 커널 소스가 없으면, 컴파일 시에 오류가 발생할 것이다. 이때에는 현재 부팅한 커널의 소스의 링크를 걸던지, 아니면, 따로 커널 컴파일을 해서 링크를 걸어야 한다.
#cd /usr/src/ #ln -s linux-2.4.20-8 linux #ln -s linux-2.4.20-8 linux-2.4
구체적인 방법은 위와 같다.
오류없이 설치되었다면, 설치된 경로의 bin 디렉토리에 가면, 'arm-linux-gdb' 파일이 생성되었을 것이다.
타겟 설정
호스트 설정에 비하면 타겟 설정은 좀더 복잡하다고 하겠다. 앞에서의 과정이 호스트에서 사용할 'arm-linux-gdb' 파일을 생성했다고 한다면, 여기서는 타겟에서 사용할 'gdbserver' 를 생성해야 한다. 물론 타겟에서 사용할 것이기 때문에, 크로스 컴파일러를 사용해서 컴파일해야 한다.
초반에서 언급한대로, 여기서는 휴인스에서 제공하는 크로스 컴파일러를 사용했다.
이에 대한 설치 방법은 생략하겠다.
#cd gdb-6.5 #cd gdb/gdbserver #export CC=arm-linux-gcc #./configure --target=arm-linux --host=arm-linux #make
앞에서의 호스트 설정에서 컴파일 했던 GDB 소스 디렉토리를 그대로 사용한다. 오류없이 컴파일 되었다면, 'gdbserver' 이 생성되었을 것이다.
이 파일을 이제 타겟보드에 올려야 한다. 다음은 타겟에서 실행한 모습이다.
# ./gdbserver Usage: gdbserver COMM PROG [ARGS ...] gdbserver COMM --attach PID COMM may either be a tty device (for serial debugging), or HOST:PORT to listen for a TCP connection.
이제 디버깅할 모든 준비가 끝났다.
디버깅 하기
다음은 디버깅에 사용할 예제 소스이다.
#include <stdio.h> int add(int a, int b); int sub(int a, int b); int main() { int x=0; int y=0; printf("start!!!\n"); x = add(3,2); printf("X is %d\n", x); y = sub(4,2); printf("Y is %d\n", y); return 0; } int add(int a, int b) { int c=0; c = a+b; return c; } int sub(int a, int b) { int c=0; c = a-b; return c; }
디버깅 할 수 있도록 다음과 같이 컴파일한다.
#arm-linux-gcc -g -o test test.c
디버깅의 최종적인 구성을 보자면, 아래와 같다.
호스트 | arm-linux-gdb, test(arm object) |
타겟 | gdbserver, test(arm object) |
이기종의 디버깅은 LAN 을 통해서 이루어진다. 동작 순서는 다음과 같다.
- 타겟에서 gdbserver 를 이용해서 디버깅 할 프로그램과 특정 포트를 open 시킨다. (ex : 8181)
- 호스트에서 디버깅할 프로그램을 이용해서 gdb 를 구동시킨다. 호스트에서 디버깅할 프로그램이 필요한 것은 심볼 테이블을 읽어들이기 위함이다.
- 호스트에서 타겟의 지정된 포트에 접속한다.
- Breakpoint 를 지정하고, 'c' 명령어를 이용해서 디버깅을 한다.
타겟에서 작업들
#./gdbserver 192.168.0.2:8181 test Process test created; pid = 83 Listening on port 8181
호스트에서의 작업들
#./arm-linux-gdb test (gdb) target remote 192.168.0.2:8181 (gdb) b main (gdb) c