AOSP 4.4_r1 의 네이티브 코드를 gdb 로 디버깅 해보자!!
- 안드로이드 어플리케이션을 실행할 때 사용되는 네이티브 코드(libbinder등) 은 어떻게 디버깅 할 수 있을까?
- 안드로이드 단말이나 에뮬레이터의
/system/bin/
아래에 있는 바이너리들(service
,am
,pm
등등) 어떻게 디버깅할 수 있을까?
- 단말이나 에뮬레이터를 타겟으로 잘(!?) 빌드된 AOSP
- AOSP 로 빌드된 소스가 포팅된 단말이나, 에뮬레이터
칸드로이드 슬로우부트 님의 문서 9 페이지부터 참고해서 진행했다.
우선 단말이나 에뮬레이터에서 gdbserver
를 실행하고 디버깅 하고자 하는 네이티브 코드를 담고 있는 pid를 인자로 전달.
$ gdbserver :5039 --attach <pid_of_process>
호스트 pc 에서 adb forward
를 통해 호스트의 5039 포트가 단말의 5039로 전달되도록 설정하고 arm-eabi-gdb
를 실행하였다. 이때 디버그 심볼의 경로를 명시.
$ adb forward tcp:5039 tcp:5039
$ cd android/prebuilt/linux-x86/toolchain/arm-eabi-4.4.3/bin
$ ./arm-eabi-gdb ~/workspace/android-4.4_r1/out/target/product/generic/symbols/system/lib/<symbol>
gdb
가 시작되면 패스를 설정하고 target 을 설정한다.
(gdb) set solib-absolute-prefix ~/workspace/android-4.4_r1/out/target/product/generic/symbols
(gdb) set solib-search-path ~/workspace/android-4.4_r1/out/target/product/generic/symbols/system/lib/
(gdb) target remote :5039
(gdb) c
하지만, 마지막에 c
(continue
) 실행이후 심볼을 찾지 못했다는 메시지와 함께 더이상 진행되지 않았다. 이게 성공한다고 하더라도, 매번 gdb
를 연결할 때마다, 이렇게 경로를 명시해야 된다면 😱
- ubuntu 12.04, AOSP 4.4_r1
- aosp_arm-eng 타겟으로 빌드 완료
aosp-env 참고!!
build/envsetup.sh
에 gdbclient
명령어가 있다. 이 명령어를 활용하면 앞서 설정했던 경로나 gdb 바이너리를 선택하는 과정을 스크립트를 통해 자동화 시켜준다.
gdbclient
는 언제부터 작성된 거지?build/envsetup.sh
를 blame 해보니 꽤 오래전부터 존재했다는 것을 알 수 있다.
gdbclient
를 사용하기 위해서는 build/envsetup
을 로드하고, 빌드 타겟까지 lunch
를 통해 지정되어 있어야 한다. 빌드 타겟설정시 선언되는 경로가 gdbclient
에서 사용하기 때문이다.
$ . build/envsteup.sh
$ lunch
gdbclient
를 시작하기 전 단말이나 에뮬레이터에서 gdbserver
를 시작해야 한다. 시작할때 이미 실행된 프로세스에 gdbserver
를 붙이기 위해서는 --attach
옵션을 통해 pid를 전달하자.
$ gdbserver :5039 --attach <pid_of_process>
$ adb forward --list
$ adb forward tcp:5039 tcp:5039
$ gdbclient
gdbclient
명령어를 살펴보면 필요한 설정들이 다 있다. 전달된 인자에 따라서 adb forward
설정이나, adb shell gdbserver
실행까지 다해준다. 그리고 gdbclient.cmds
를 열어 필요한 경로 설정도 다해준다. gdbclient
는 실행할 때 기본값으로 app_process
심볼을 가져온다.
adb shell
로 쉘에 진입했을 때, 시스템 서비스를 컨트롤 할 수 있는 도구들(am
, pm
, wm
등등)을 gdb
로 디버깅할 수 있다. 이 도구들의 코드는 frameworks/base/cmds
또는 frameworks/native/cmds
에서 찾을 수 있다.
이 도구들의 코드는 시스템 서비스를 만들거나 해킹하고자 할때 좋은 출발점이 될 것 같다.
$ adb shell
> gdbserver localhost:5039 service list
$ adb forward tcp:5039 tcp:5039
$ gdbclient service
gdbclient 에 인자(service
)를 주지 않으면 app_process
를 디버깅하도록 설정되니 주의하자. 이건 build/envsetup.sh
에서 찾을 수 있다.
service list
는 현재 안드로이드 운영체제에서 실행되고 있는 시스템 서비스의 리스트를 출력하는 명령어이다.
gdb
가 실행되면, service
바이너리의 main
함수에 브레이크 포인트를 걸고 c
(continue)명령을 입력하면 frameworks/native/cmds/service/service.cpp
의 main
함수에서 멈추는 것을 알 수 있다. 이제 n
(next) 를 입력해가면서 이 소스를 한줄한줄 디버깅할 수 있다.
(gdb) b main
(gdb) c
reakpoint 1, main (argc=2, argv=0xbeecfaf4)
at frameworks/native/cmds/service/service.cpp:70
70 {
(gdb) l
(gdb) n
진행되는 과정을 영상으로 확인하고 싶다면 여기로!!
브라우저의 영문 폰트가 고정폭 폰트로 설정되어 있어야 잘 보입니다 😄
신기한건, 앞선 예제에서 service
를 인자로 주지않고, gdbclient
를 실행하면 libbinder
에 포함된 코드도 디버깅이 된다는거. 왜 그런 것일까?
app_process
를 컴파일 하면서 libbinder.so
가 링크되기 때문. 이 내용은 frameworks/base/cmds/app_process/Android.mk
에 보면 shared library
형태로 libbinder
를 사용하는 것을 확인할 수 있다.
- http://www.unknownroad.com/rtfm/gdbtut/gdbtoc.html 여기에 꼭 필요한 내용만 잘 정리되어 있다.
- lunch 를 실행하면 path에
android/prebuilt/linux-x86/toolchain/arm-eabi-*/bin
가 추가되어arm-eabi-gdb
,arm-eabi-gdbtui
를 사용할 수 있게된다.arm-eabi-gdbtui
는 화면을 나눠 위쪽엔 코드가, 아래쪽엔 gdb 명령창이 나온다.