맥 OS X 커널 디버깅 방법을 간략히 정리해 본다.
참고 사이트에 동일한 내용이 나와 있지만 직접 해 본 내용을 적어 놓는 것도 의미가 있으므로... ㅋㅋ
사전 준비 사항은 다음과 같다.
1. 디버거 맥과 디버기 맥은 동일한 OS X 버전이어야 한다.
나는 두대의 OS X 10.7 (Lion) 맥북으로 테스트했다.
2. 디버거 맥과 디버기 맥은 동일한 네트워크에 있어야 한다.
커널 디버깅이 네트워크로 지원되기 때문에 같은 망 안에 있어야 한다.
네트워크 커널 디버깅은 지금까지 윈도우 커널도 지원하지 않았던 기능이라 맥의 일부 진보함을 엿볼수 있는 부분이다.
3. 디버거 맥에 Kernel Debug Kit을 다운로드 받아 마운트해야 한다.
디버기 맥 OS X 버전과 일치하는 것을 다운로드해 사용해야 한다.
OS X 10.7.4(11E53)
다음과 같이 연결을 진행한다.
1. 디버기 부팅 설정
커널 디버깅 옵션을 켜고 부팅되도록 디버기를 설정한다.
$ sudo nvram boot-args="debug=0x144 -v"
Password:
명령후 재부팅한다.
부팅 옵션(0x144) 의 의미는 다음과 같다.
Symbolic Name |
Flag |
Meaning |
DB_HALT |
0x01 |
Halt at boot-time and wait for debugger attach (gdb). (부팅이 중지되며 디버거를 기다린다.) |
DB_PRT |
0x02 |
Send kernel debugging printf output to console. |
DB_NMI |
0x04 |
Drop into debugger on NMI (Command–Power, Command-Option-Control-Shift-Escape, or interrupt switch). (NMI 발생시 디버거로 진입한다.) |
DB_KPRT |
0x08 |
Send kernel debugging kprintf output to serial port. |
DB_KDB |
0x10 |
Make ddb (kdb) the default debugger (requires a custom kernel). |
DB_SLOG |
0x20 |
Output certain diagnostic info to the system log. |
DB_ARP |
0x40 |
Allow debugger to ARP and route (allows debugging across routers and removes the need for a permanent ARP entry, but is a potential security hole)—not available in all kernels. (기본적으로는 IP와 MAC 주소를 고정해 놔야 디버거가 디버기를 찾아내는데 이럴 필요를 없애준다.) |
DB_KDP_BP_DIS |
0x80 |
Support old versions of gdb on newer systems. |
DB_LOG_PI_SCRN | 0x100 | Disable graphical panic dialog. (크래시 발생시 나타나는 기본 메시지 화면이 나타나지 않게 한다.) |
DB_KERN_DUMP_ON_NMI |
0x0800 |
Causes the kernel to core dump when the user triggers an NMI. |
DB_DBG_POST_CORE | 0x1000 | Controls the kernel's behavior after dumping core in response to an NMI (DB_KERN_DUMP_ON_NMI). If the user triggers an NMI and this flag is clear, the kernel will dump core and then continue. Conversely, if this flag is set the kernel will dump core and then wait for a debugger connection. |
DB_PANICLOG_DUMP | 0x2000 | Controls whether the kernel dumps a full core (if the flag is clear) or simply a panic log (if the flag is set). |
2. 디버거에서 gdb 실행
다음 명령으로 gdb를 실행하고 커널 디버깅을 초기화 한다.
(최근 맥은 대부분 64비트로 부팅하기 때문에 x86_64로 설정해 준다. 32비트라면 i386으로 설정한다.)
$ gdb -arch x86_64 /Volumes/KernelDebugKit/mach_kernel
GNU gdb 6.3.50-20050815 (Apple version gdb-1752) (Sat Jan 28 03:02:46 UTC 2012)
Copyright 2004 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 "x86_64-apple-darwin"...
gdb 내부에서는 다음과 같이 커널 디버깅을 초기화 한다.
(gdb) source /Volumes/KernelDebugKit/kgmacros
Loading Kernel GDB Macros package. Type "help kgm" for more info.
(gdb) target remote-kdp
3. 드라이버 빌드 및 복사, 로드
개발장비(디버거)에서 드라이버를 빌드하고 테스트장비(디버기)로 복사한다.
테스트장비에서 드라이버를 로드한다.
$ sudo kextutil MyDrv.kext
참고 자료에서는 바로 크래시가 발생하는 드라이버를 사용하기 때문에 시스템이 바로 죽고 디버거를 기다린다.
하지만 여기서는 문제없는 정상적인 드라이버인 MyDrv.kext를 사용해 본다.
4. 디버기 대기 상태 진입
시스템이 죽었다면 바로 디버거를 기다리는 상태가 되겠지만 정상 운영중이기 때문에 커널 디버거를 붙이려면 디버거 대기 상태로 진입해야 한다.
디버기(테스트장비)에서 Command + Power(전원버튼)을 눌러 NMI를 일으키면 다음과 같이 대기 상태가 된다.
왼쪽 상단 화면을 무식하게 깨뜨리면서 텍스트가 출력된다. ^^
(마지막 Connected to remote debugger 라인은 5번 과정이 완료되면 출력된다.)
5. 디버거 연결
gdb에서 다음과 같이 연결을 시도한다.
(gdb) kdp-reattach 172.x.x.x
Connected.
연결이 되면 위에서 보인 디버기 화면의 마지막 라인이 출력된다.
Connected to remote debugger.
6. 디버거에서 드라이버 심볼 생성
참고자료에는 드라이버의 심볼 파일을 생성해서 사용하는 것으로 나와 있다.
윈도우처럼 빌드시 심볼을 생성해 놓고 사용하고 싶은데 아직 찾아보지 못한 관계로 자료에서 가이드하는대로 진행한다.
먼저 연결된 gdb에서 다음 명령으로 드라이버의 주소와 크기를 조회한다.
(gdb) showallkmods
kmod_info address size id refs version name
0xffffff7f80798c60 0xffffff7f80791000 0x0000000000008000 129 0 1.4.2 com.apple.filesystems.cd9660
0xffffff7f81834970 0xffffff7f81803000 0x0000000000035000 128 0 1.7.2 com.apple.filesystems.smbfs
0xffffff7f81e74260 0xffffff7f81e6b000 0x000000000000e000 127 0 70.12 com.apple.driver.AppleBluetoothMultitouch
0xffffff7f81a9f120 0xffffff7f81a97000 0x0000000000012000 126 1 231.4 com.apple.driver.AppleMultitouchDriver
0xffffff7f80da0920 0xffffff7f80d93000 0x0000000000012000 125 1 4.0.5f11 com.apple.driver.IOBluetoothHIDDriver
0xffffff7f81c5bcd0 0xffffff7f81c57000 0x0000000000005000 124 0 1.9.5d0 com.apple.driver.AppleHWSensor
0xffffff7f80fccc30 0xffffff7f80fc5000 0x0000000000008000 123 0 1 com.greemate.driver.MyDrv
0xffffff7f81aabc90 0xffffff7f81aa9000 0x0000000000003000 122 0 122 com.apple.driver.AppleMikeyHIDDriver
0xffffff7f811c2550 0xffffff7f811be000 0x0000000000005000 121 0 1.59 com.apple.driver.AudioAUUC
...
출력된 리스트 중에서 MyDrv.kext를 찾아서 주소와 크기를 찾는다.
다음엔 심볼파일을 생성하기 위해 /tmp에 MyDrv.kext를 복사해 놓고 명령창에서 다음과 같이 입력한다.
$ sudo kextutil -s /tmp -n -arch x86_64 -k /Volumes/KernelDebugKit/mach_kernel -e -r /Volumes/KernelDebugKit /tmp/MyDrv.kext
Password:
/tmp/MyDrv.kext appears to be loadable (not including linkage for on-disk libraries).
Enter the hexadecimal load addresses for these extensions
(press Return to skip symbol generation for an extension):
com.greemate.driver.MyDrv: 0xffffff7f80fccc30
마지막 라인에서 드라이버 주소를 입력하면 /tmp 에 심볼 파일이 생성된다.
7. 디버거에서 드라이버 심볼 로드
다시 gdb에서 심볼 파일 경로를 지정한다.
(gdb) set kext-symbol-file-path /tmp
디버깅할 드라이버를 추가해 준다.
(gdb) add-kext /tmp/MyDrv.kext
Reading symbols from /private/tmp/com.greemate.driver.MyDrv.sym...done.
이 때 심볼 파일이 로드된다.
8. 디버깅
MyDrv.kext 함수중 디버깅하고 싶은 함수에 브레이크 포인트를 건다.
(gdb) break CMyDrv::foo
Breakpoint 1 at 0xffffff7f80fcc810: file MyDrv.h, line 157.
브레이크 포인트가 걸리면 bt 명령으로 콜스택을 확인한다.
(gdb) bt
#0 0xffffff7f80fcc810 in CMyDrv::foo () at MyDrv.h: 157
#1 0xffffff7f809d7b5d in ?? ()
#2 0xffffff7f809d4371 in ?? ()
#3 0xffffff7f809d4418 in ?? ()
#4 0xffffff800063c5b6 in nfs_socket_search_cleanup (nss=0xffffff80008d8be8) at nfs_socket.c:620
#5 0xffffff800063b330 in nfs_connect_upcall (so=0x121, arg=0xffffff80120ebe38, waitflag=-128) at nfs_socket.c:351
#6 0xffffff800063b1d4 in nfs_connect_upcall (so=0xffffff80008bef20, arg=0xffffff8011bba880, waitflag=-128) at nfs_socket.c:322
9. 결론
일단 이정도까지 진행하면 기본적인 디버깅은 진행할 수 있다.
gdb 명령어가 WinDbg와 달라 아직 명령어에 익숙하지 않다.
좀 더 찾아보고 적응해야 하는데 정리되는 대로 공유할 예정이다. ^^
[참고자료]
1. Debugging a Kernel Extension with GDB