'Mac OS X'에 해당되는 글 1건

  1. 2014.01.12 Mac 커널 디버깅 3
Mac OS X2014. 1. 12. 20:58
반응형

맥 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.


(gdb) info b
Num Type           Disp Enb Address            What
1   breakpoint     keep y   0xffffff7f80fcc810 in CMyDrv::foo() at MyDrv.h:157



멈춰있던 디버기를 진행시킨다.

(gdb) c
Continuing.


디버기에서 함수가 호출될 수 있는 동작을 취한다.

브레이크 포인트가 걸리면 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



8. 디버깅 종료

디버거를 종료하고 싶다면 다음과 같이 끝내주면 된다.

(gdb) quit


9. 결론


일단 이정도까지 진행하면 기본적인 디버깅은 진행할 수 있다.

gdb 명령어가 WinDbg와 달라 아직 명령어에 익숙하지 않다. 

좀 더 찾아보고 적응해야 하는데 정리되는 대로 공유할 예정이다. ^^



[참고자료]

1. Debugging a Kernel Extension with GDB

2. Kernel Programming Guide


반응형
Posted by GreeMate