This tutorial describes how to prepare to debug a device driver for Darwin/Mac OS X. You will learn how to set up a two-machine debugging environment and how to start using GDB, a command-line debugger, to perform remote debugging.
The tutorial assumes that you are working in a Mac OS X development environment.
Although this tutorial is written with a device driver as the example, the steps for debugging are similar for debugging any type of kernel extension (KEXT). If you wish, you can substitute your own code for the example. Note, however, that you may encounter a few inconsistencies. For example, examples of GDB commands may be dependent on the underlying source code language—I/O Kit extensions (drivers) use C++; the GDB commands for C may differ.
Preparation
Roadmap
On Target Machine, Enable Kernel Debugging
On Development Machine, Set Up a Permanent Network Connection
Create a Symbol File
On Development Machine, Import the Symbol File
On Development Machine, Start GDB
On Target Machine, Break into Kernel Debugging Mode
On Development Machine, Attach to the Target Machine
On Development Machine, Set Breakpoints in GDB
On Target Machine, Start Running the Device Driver
On Development Machine, Control the Driver With GDB
Stop the Debugger
On Target Machine, Unload the Driver
Postmortem Debugging
Where to Go Next
Before you begin this tutorial, make sure you have met the following requirements.
Be sure you understand the material presented in the KEXT and driver development tutorials.
The tutorials “Hello Kernel: Creating a Kernel Extension With Xcode” and “Hello I/O Kit: Creating a Device Driver With Xcode” describe how to create a kernel extension's project, correct code errors, and how to create a simple I/O Kit device driver. This tutorial uses the example from “HelloIOKit”. Be sure that you have completed these tutorials (or are familiar with KEXT or driver development in Mac OS X) before beginning this one.
For remote debugging, you need two machines. On the development machine, you create, build, and, for this tutorial, debug your driver. On the target machine, you load and run the driver.
Before you begin this tutorial, you should prepare the two machines. Note that:
Both machines must be running the same version of Mac OS X.
Both machines must be connected via Ethernet using the built-in Ethernet ports.
If you’re running a version of Mac OS X earlier than Mac OS X version 10.3, both machines must be on the same subnet.
You must have login access to both machines; this tutorial assumes you are logging in as an administrator of your machine (in this tutorial, the user admin
). You will need root
privileges for some of the commands so you will be using the sudo command which allows permitted users to execute a given command as root
. If you are using another account, substitute that account name in the examples that follow, but make sure that account has administrative access.
Make sure that Personal File Sharing and FTP Access are enabled for the target machine. Use the Sharing panel in the System Preferences (click System Preferences in the Apple menu or in the Dock) to enable FTP Access and Personal File Sharing.
If the target machine has Apple Remote Desktop enabled, you should disable it before you continue with this tutorial. To do this, open System Preferences, click Sharing, and uncheck the Apple Remote Desktop checkbox in the Services pane.
Transfer a copy of your KEXT to the target machine.
Before you can begin to debug your driver (and each time you rebuild it), you must transfer a copy of your KEXT to the target machine. You can do this in any of several ways. For example:
Use File Sharing to transfer your KEXT over the network.
Use the tar and ftp commands (from within the Terminal application), to package your KEXT and transfer it over the network.
You can also automate this process using ssh. This tutorial does not tell you how to do this, but you can find resources on the web that describe the process.
This tutorial has many steps. It is important to keep the two machines clear in your mind as you work through the steps. It may help if you take a piece of paper, tear it in half, and write “Development” on one piece and “Target” on the other. Then place the pieces of paper next to the two machines.
You will be moving back and forth between the two machines many times. Figure 1 illustrates the steps you will take for each machine.
After reading “Preparation,” you should have already prepared the two machines and attached them to the network. The next two steps will enable kernel debugging and set up a permanent network connection between the two machines.
Once these steps are completed, you will not need to repeat them, even if you need to start over part way through the tutorial. The next set of steps prepare the driver for debugging, set up GDB, and attach the two machines to begin debugging your driver. If you need to start over part way through the tutorial, you should repeat all of these steps from the beginning.
The last two steps stop the debugger and unload the driver. If you need to start over part way through, you may need to skip ahead to these steps to reset the environment completely, then start again at “Create a Symbol File.”
In this step and the next, you will set up the two-machine environment. These two steps will enable kernel debugging and set up a permanent network connection between the two machines.
By default, Darwin/Mac OS X does not let you debug the kernel. Before you can debug your driver, you must first enable kernel debugging. On the target machine, do the following:
Start the Terminal application. From a Finder window, locate and launch the Terminal application, found at /Applications/Utilities/Terminal
.
Set the kernel debug flags.
To enable kernel debugging, you will be setting an NVRAM (non-volatile random access memory) variable. To do this, you must use the sudo command which gives permitted users the ability to execute a given command as root
. When sudo prompts you for a password, enter your admin
password. (Note that nothing is displayed as you type the password.)
The sudo command will allow you to execute commands with root
privileges without re-entering your password for a few minutes (usually 5) but after that time is up, it will ask you for your password again. This tutorial does not show the password prompt every time the sudo command is used.
If you're using a beige Power Macintosh G3 and you’re running a version of Mac OS X prior to 10.1, enter this command:
$ sudo nvram boot-command="0 bootr debug=0x14e" |
If your target machine is a PowerPC-based Macintosh model not listed above or an Intel-based Macintosh, enter this command:
$ sudo nvram boot-args="debug=0x14e" |
Password: |
For more information on debugging flags, see Building and Debugging Kernels in Kernel Programming Guide.
Restart the computer.
The computer restarts and displays the login screen.
Your development and target machines must be continuously connected by a reliable network connection. In this section, you will create such a connection.
Note: If your target machine is running Mac OS X 10.3 or later and is configured with the 0x40 debug flag set (DB_ARP
), you can skip this section.
After the target machine has restarted, do the following steps on the development machine:
Start the Terminal application. From a Finder window, locate and launch the Terminal application, found at /Applications/Utilities/Terminal
.
Make sure your development machine can connect to your target machine.
Use the ping command with your target machine’s hostname or IP address. For example:
$ ping -c 1 target.apple.com |
or
$ ping -c 1 192.102.207.119 |
This command makes sure your development machine can reach your target machine and creates a temporary connection between them. The -c 1
option tells ping to stop after sending (and receiving) one ICMP (Internet Control Message Protocol) packet.
You should see output similar to this:
PING target.my-company.com (192.102.207.119): 56 data bytes |
64 bytes from 192.102.207.119: icmp_seq=0 ttl=255 time=2.099 ms |
--- target.apple.com ping statistics --- |
1 packets transmitted, 1 packets received, 0% packet loss |
round-trip min/avg/max = 2.099/2.099/2.099 ms |
If you don't already know it, determine the hardware Ethernet address of the target machine.
Enter this command:
$ arp -a |
The arp command with the -a
option lists the static hardware Ethernet addresses of all machines your development machine has recently accessed. Make a note of the entry for your target machine's address. Because this address is stored in the hardware, you should keep track of this number for future debugging sessions (or for debugging possible kernel panics).
For example, you should see output similar to this:
aniil33 (1192.102.207.102) at 0:0:f:0:86:c3 |
target (192.102.207.119) at 0:5:2:b0:b3:20 |
dev (192.102.207.124) at 0:5:2:59:2f:20 |
In this example, the hardware Ethernet address to remember is 0:5:2:b0:b3:20
.
Remove the current, temporary, connection between your development and target machines. Use the arp command with your target machine’s hostname. Because you’ll be using the -d
option to delete the entry for the target machine, this command must be executed as root
, so use the sudo command.
For example:
$ sudo arp -d target.apple.com |
You should see output like this:
target.apple.com (192.102.207.119) deleted |
Create a new, permanent, connection between your development and target machines. Use the arp command with the target machine’s hostname and Ethernet address. The arp command with the -s
option must be executed as root
, so use the sudo command.
For example:
$ sudo arp -s target.apple.com 0:5:2:b0:b3:20 |
You can copy and paste the Ethernet address from the previous output in the Terminal window.
Make sure the connection was made correctly.
Enter this command:
$ arp -a |
Make sure the entry for your target machine now contains the word “permanent.”
You should see output similar to this:
niil33 (192.102.207.102) at 0:0:f:0:86:c3 |
target (192.102.207.119) at 0:5:2:b0:b3:20 permanent |
dev (192.102.207.124) at 0:5:2:59:2f:20 |
The previous steps prepared the two machines to communicate with each other. The next steps will load the driver, prepare and transfer a symbol file, and start the debugger.
Warning: If you make a mistake in any of the following steps, or need to retrace any steps, you should begin again at this step.
There are two ways to create a symbol file. If the target machine is running, you can generate the symbol file on the target machine itself. If the target machine is crashed, you must generate the symbol file on the host machine. This section describes both methods.
On the target machine, do the following:
At your option, either launch Terminal as an admin user or login as >console
(with a blank password), then
If you logged in as >console
, at the next login prompt, log in as an admin user.
For example:
Darwin/BSD (dev) (console) |
login: admin |
Password for admin: |
admin$ |
Copy your KEXT to /tmp
.
Because you will be loading your KEXT into the kernel, your KEXT must be owned by the user root
and the group wheel
and the folders and files of the KEXT bundle must have their permissions set so that they are not writable by any user other than the super user. If you haven't already done so, use the sudo command to copy the KEXT binary (HelloIOKit.kext
) to the /tmp
directory so you can load and unload the KEXT from there. Using sudo to copy the KEXT binary gives the KEXT the super user's ownership and permissions and leaves the original KEXT alone so you can revise and save it as you choose.
Note: Every time you make changes to your KEXT and rebuild it, you need to repeat this step to copy the new version to the /tmp
directory to load and debug it.
For example:
$ sudo cp -R HelloIOKit.kext /tmp |
Warning: If you use tab-completion to avoid typing all of HelloIOKit.kext
, you’ll get a “/” after the KEXT name. Be sure to delete this slash before you press Return. If you don’t, the cp command will copy only the contents of the HelloIOKit.kext
directory to /tmp
, instead of copying the directory and its entire subtree.
For debugging purposes, you first use kextload with some options to load the driver and create a symbol file for it, but not start the driver. You'll register the driver and start it running (again using kextload) in a later step, “On Target Machine, Start Running the Device Driver.” The kextload command must be run as root
, so you must also use the sudo command.
Warning: In the KEXT development tutorials, “Hello Kernel: Creating a Kernel Extension With Xcode” and “Hello I/O Kit: Creating a Device Driver With Xcode,” you used kextload to load your KEXT and start it running all in one step. Now you will break this step in two, using different options to kextload each time.
Load the driver (without starting it) and create a symbol file for it.
For example, enter the command:
$ sudo kextload -ls /tmp HelloIOKit.kext |
The -l
(this is a lower-case “L”) option tells kextload to load the KEXT's code into the kernel but not to start I/O Kit matching, which would start the KEXT running. The -s
option tells kextload to create symbol files in /tmp
for the KEXT and any of its dependencies. To ensure uniqueness the symbol files are named after each KEXT's CFBundleIdentifier, plus a .sym
or .dSYM
extension.
For the most part, the two symbol file formats are equivalent. For this reason, this document does not distinguish between them except where behavior differs.
Do not rename the symbol file if you are debugging with a DWARF symbol file (.dSYM
). If you are debugging with a STABS symbol file (.sym
), you may rename the file so long as the name still ends with .sym
.
The symbol file contains information that GDB needs to debug your driver. You must create the symbol file for a loaded driver, then copy the file to the development machine where it will be used. Note that you must recreate the symbol file again if you unload (and reload) the driver. This is because the KEXT may be loaded at a different address each time and the symbol files contain the actual virtual address of each symbol.
Optionally, you can rename the symbol file.
For example:
$ mv /tmp/com.MyTutorial.driver.HelloIOKit.sym /tmp/HelloIOKit.sym |
To create a symbol file on the host machine, you must have a copy of the kernel extension on the host machine. The host machine must have the same architecture as the target machine (for example, two Intel machines). You must also have the kernel debug kit that correspondes with the version of Mac OS X installed on the target machine.
Create the symbol file.
sudo kextload -n -k /Volumes/KernelDebugKit/mach_kernel -s /tmp/syms/ HelloIOKit.kext |
Adjust the path to the kernel debug kit and your kernel extension as appropriate.
The kextload
tool will now prompt you for the load addresses of your KEXT and any KEXT upon which it depends. Enter the addresses as requested.
If the target machine is still running, you can obtain a full list by running kextstat
(with no arguments) on the target machine. See the manual page for kextstat(8)
for more information.
In the case of a non-graphical kernel panic, you should find a list of these address on screen.
In the case of a graphical kernel panic or a hang, you can get this information by connecting to the target machine using GDB as described in “Postmortem Debugging.”
When you finish, you should see a symbol file in /tmp/syms
.
There’s no step 3!
On the development machine, import the symbol file (.sym
or .dSYM)
from the target machine, for use with GDB. You can do this by copying the file to a keychain drive or an external hard drive, or by using the ftp(1)
(file transfer protocol) or scp(1)
(secure copy) command. For security reasons, the scp
command is recommended over the ftp
command.
If you are debugging a driver built with DWARF symbols (a .dSYM
file), you must also copy the kernel extension itself. The easiest way to do this is to create a ZIP archive of the KEXT in Finder or use tar on the command line.
For example, the following two commands create an archive of a KEXT and extract that archive, respectively:
tar -czf mykext.tar.gz mykext.kext # create archive |
tar -xzf mykext.tar.gz # extract archive |
To use the scp
command, you need an account on the target machine and you need to have Remote Login enabled in the Sharing System Preferences pane. Then, issue the following commands:
cd /path/to/store/the/symbol/file/ |
scp username@target.apple.com:/path/to/remote/file.sym . |
Change the remote username, host name, and paths as appropriate. The scp command then asks you for the remote password. Type the password, and a few seconds later, you should have a copy of the symbol file in the destination directory on your development machine.
To use the ftp
command, you need an account on the target machine and you need to enable FTP in the Sharing System Preferences pane. A sample ftp session is shown below. Enter the ftp commands as shown.
Warning: Be sure to use the actual name or IP address of the target machine; in this example it is target.apple.com
. Be sure to log in with your actual account name; this example uses admin
.
$ ftp target.apple.com |
Connected to target.apple.com. |
220 target.apple.com FTP server (Version 6.00) ready. |
Name (dev:admin): admin |
331 Password required for admin. |
Password: |
230- Welcome to Darwin! |
230 User admin logged in. |
Remote system type is BSD. |
ftp> bin |
200 Type set to I. |
ftp> get /tmp/com.MyTutorial.driver.HelloIOKit.sym |
local: /tmp/com.MyTutorial.driver.HelloIOKit.sym remote: /tmp/com.MyTutorial.driver.HelloIOKit.sym |
200 PORT command successful. |
150 Opening BINARY mode data connection for '/tmp/com.MyTutorial.driver.HelloIOKit.sym' |
226 Transfer complete. |
180356 bytes received in 0.15 seconds (1.15 MB/s) |
ftp> bye |
Note that you must recreate (and import) the symbol file again if you unload (and reload) the driver on the target machine. You should also note that any files in the /tmp
directory are temporary and will be deleted whenever Darwin/Mac OS X is restarted.
Now you can start GDB. On the development machine, do the following from the Terminal application:
Start the debugger on the kernel.
Enter this command:
$ gdb -arch ppc /mach_kernel |
Substitute -arch i386
if your debug target is an Intel-based Macintosh. You should see output like this:
GNU gdb 4.18-20000320 (Apple version gdb-172) |
Copyright 1998 Free Software Foundation, Inc. |
... |
(gdb) |
Load the symbol file you created and transferred.
If you are debugging a kernel extension compiled with DWARF symbols, at the GDB prompt, use the add-kext command with the name of the KEXT.
Note: When debugging with DWARF symbols (a file ending in .dSYM
), you must place the debug symbols file in the same directory as the KEXT.
For example:
(gdb) add-kext /tmp/com.MyTutorial.driver.HelloIOKit.kext |
If you are debugging a kernel extension compiled with STABS symbols, at the GDB prompt, use the add-symbol-file command with the name of the symbol file you’ve created.
For example:
(gdb) add-symbol-file /tmp/com.MyTutorial.driver.HelloIOKit.sym |
Tell the debugger that you're remotely debugging a device driver.
At the GDB prompt, enter this command:
(gdb) target remote-kdp |
Before you can connect GDB to the target machine, you must break into kernel debugging mode. There are four ways to break into kernel debugging mode.
You can force a break into the debugger by briefly pressing the power button on the machine. Because you enabled kernel debugging in “On Target Machine, Enable Kernel Debugging,” briefly pressing the power button sends a nonmaskable interrupt (NMI) instead of sleeping or waking the machine. You will use this method in this tutorial.
On some machines, you can force a break into the debugger using the programmer's button (check your computer manual for its location) to send an NMI.
If your keyboard contains a power button, you can force a break into the debugger from the keyboard.
To use this method, you need to hold a combination of keys for three seconds. To estimate three seconds, try counting aloud “one thousand one, one thousand two, one thousand three”.
On a USB keyboard, or if you’re using a PowerBook or iBook , hold down the Command (or Apple) key and the Power button.
If you’re running Mac OS X version 10.4 or later, hold down the following five keys: Command, Option, Control, Shift, and Escape.
On an ADB keyboard, hold down Control and Power.
Be sure to hold down the keys for only three seconds. You could reboot the machine if you hold the keys down for too long.
In Mac OS X 10.4 and later, if you are using a USB keyboard, you can use the key combination Command-Option (Alt)-Control-Shift-Escape.
You can put a call to PE_enter_debugger
into your code; your driver will enter the debugger when it reaches this point. For example, you could add this line to your driver's init
method:
PE_enter_debugger("debug") |
Note: Unless you’re in Console mode, it may be difficult to tell when the kernel stops and the target machine is in kernel debugging mode. One way to tell is to enable the seconds display in the menu bar clock. When the seconds stop counting, the kernel is stopped. To enable the seconds display, open System Preferences and click Date & Time. Select the Clock tab and check the box next to Display the time with seconds.
On the target machine, do the following:
Break into the debugger. Briefly press the power button on the machine.
The kernel stops and the machine waits for a remote debugger connection.
If you’re in Console mode, you’ll see the following message on the screen:
ethernet MAC address: 0:5:2:b0:b3:20 |
ip address: 192.102.207.119 |
If the seconds don’t stop counting (or if you don't see the “Waiting...” message in Console mode), briefly press the power button again.
Immediately move to the development machine and attach to the target machine from GDB.
Now you can tell GDB to attach to the target machine. On the development machine, do the following:
Attach to the target machine. At the GDB prompt, use the attach
or kdp-reattach
macro with the target machine’s name or IP address.
For example:
(gdb) attach target.apple.com |
or |
(gdb) kdp-reattach target.apple.com |
The target machine prints:
Connected to remote debugger. |
Note that the target machine is currently unable to accept keyboard input.
This is the best place to set breakpoints in your code, before you actually run the driver on the target machine. For this tutorial, set a breakpoint at the start
method.
In GDB, set a breakpoint in the start
method.
For example:
(gdb) break 'com_MyTutorial_driver_HelloIOKit::start(IOService |
*)' |
Breakpoint 1 at 0x53d93d0: file HelloIOKit.cpp, line 54. |
Hint: You don't need to type the entire class name for the breakpoint; GDB can do name completion. Type a single quote, '
, then the first part of the name, then press the tab key. If GDB does not complete the name, you may need to type a few more characters to allow it to choose an unambiguous match. For example, type
(gdb) break 'com_<TAB> |
When you press the tab key, GDB will complete the name as far as it can, unambiguously. If it cannot complete the entire name of the method, you will need to give it more clues. For example, all of the methods in this tutorial begin with com_MyTutorial_driver_HelloIOKit
. When GDB has completed that much, it will stop. Then you can type
::start<TAB> |
GDB will finish the entry. After GDB completes the method name (with a final closing single quote), be sure to press return.
Allow the target machine to continue. On the development machine, enter the continue command at the GDB prompt. For example:
(gdb) continue |
The target machine is again able to accept keyboard input. Now you can run the driver.
Now that you have GDB attached and breakpoints set, you can start the driver running. On the target machine, do the following.
If you are not already there, move to the /tmp
directory, using the cd command. For example
$ cd /tmp |
Start the driver running, using the kextload command. Recall that you previously loaded the driver using kextload with the -l
option, which does not start I/O Kit matching for the driver, meaning that the code doesn't start running. You must now use kextload with the -m
option to finish the process and run the driver. For example:
$ sudo kextload -m HelloIOKit.kext |
The driver executes until it hits a breakpoint.
Now that GDB is running on the development machine, the target machine is attached to the debugger, and the driver is running, you can actually start debugging!
The driver executes on the target machine until it hits a breakpoint. When this happens, GDB will print some status on the development machine. For example:
Breakpoint 1, com_MyTutorial_driver_HelloIOKit::start (this=0xcdcfc0, dict=0xbcfe40) at HelloIOKit.cpp:54 |
54 HelloIOKit.cpp: No such file or directory. |
Current language: auto; currently c++ |
Now you can debug your driver (almost) as you would any other executable. However, because driver debugging happens at such a low level, you won't be able to take advantage of all of GDB’s features. For example:
You can't call a function or method in your driver.
You can't debug interrupt routines.
Kernel debug sessions don't last indefinitely. Because you must halt the target machine's kernel to use GDB, internal inconsistencies may appear that will cause the target kernel to panic or hang, forcing you to reboot the target machine.
For help with GDB, use its help command. Most GDB commands have shortcuts; for example, you can type c
instead of continue
.
Here are a few things you can do. From GDB, try the following:
Single step through some code (s command).
List a method's source code (l command).
Print the contents of a variable (p command). For example
(gdb) p res |
When you've finished debugging, you should stop the debugger and then unload the driver. Do the following:
On the target machine, break into the kernel debugger. Briefly press the power button as described in “On Target Machine, Break into Kernel Debugging Mode.”
If you’re in Console mode, you’ll see the following message on the screen:
Debugger(Programmer Key) |
On the development machine, enter this command at the GDB prompt:
(gdb) quit |
Answer yes to the prompt that appears:
The program is |
running. Exit anyway? (y or n) |
The target machine prints
Remote debugger |
disconnected |
The debugging session ends.
Note: If your target machine is running Mac OS X Server or contains a PMU (for example, a PowerBook G4 or an early G5 desktop computer), you may find that it reboots during kernel debugging or shuts down when you exit kernel debugging mode. These problems have different causes.
If a breakpoint or explicit NMI causes kernel debugger entry in the middle of a PMU transaction, the PMU transaction times out after a few milliseconds. The result is that the PMU driver and the PMU hardware are now out of sync. The resulting confusion can cause the computer to shut down or stall for a period of time after you leave the debugger.If you experience this, you may find that forcing the PMU driver to operate in polled mode fixes the problem. To do this, set the NVRAM variablepmuflags
to 1
when you enable kernel debugging (see “On Target Machine, Enable Kernel Debugging”), as shown below:$ sudo nvram boot-args="pmuflags=1"
You can set the pmuflags
variable separately, as shown above, or you can set it at the same time you set the debug
variable, as shown below:$ sudo nvram boot-args="debug=0x14e pmuflags=1"
In Mac OS X Server, the watchdog daemon enables a hardware watchdog timer. That watchdog timer is designed to reboot the server if it crashes or hangs. Because the daemon that tickles this timer every 30 seconds cannot run while you are debugging, debugging is indistinguishable from a crash or hang, and thus, the watchdog timer assumes that the computer has crashed and reboots it.To disable the watchdog timer, you must terminate the watchdog daemon in such a way that it exits cleanly and disables the hardware watchdog timer. To do this, issue the following command prior to entering the debugger:$ sudo killall -TERM watchdogtimerd
For more information, see the manual page for watchdogtimerd(8)
.On the target machine, unload the driver. Use the kextunload command. This command unloads your driver by running its termination (stop) function. For example:
$ sudo kextunload HelloIOKit.kext |
IOCatalogueTerminate(Module com.MyTutorial.driver.HelloIOKit) [0] |
done. |
The previous sections assume that the target machine is accessible and has not crashed. In some cases, you may need to debug a computer that has already crashed or frozen, and thus, running kextload to generate a symbol file is not possible.
To do this, you first need a development machine of the same architecture as the target machine (Intel if your target machine is Intel, for example). Next, you need a copy of the kernel debug kit for the version of Mac OS X that is running on the target machine.
If the target machine is frozen (as opposed to a kernel panic), drop into the debugger on the target machine as described in the section “On Target Machine, Break into Kernel Debugging Mode.”
Start the debugger on the kernel.
Enter this command:
$ gdb -arch ppc /mach_kernel |
Substitute -arch i386
if your debug target is an Intel-based Macintosh. You should see output like this:
GNU gdb 4.18-20000320 (Apple version gdb-172) |
Copyright 1998 Free Software Foundation, Inc. |
... |
(gdb) |
Tell the debugger that you're remotely debugging a device driver.
At the GDB prompt, enter this command:
(gdb) target remote-kdp |
Attach to the remote machine with the following command:
kdp-reattach 192.168.1.47 |
Substitute the actual IP number of the remote machine.
Once you have connected to the target, you must load the GDB macros from the kernel debug kit by issuing the following command:
source /Volumes/KernelDebugKit/kgmacros |
Assuming the target machine is configured to display panic text, you already have a list of KEXTs involved in the panic. If not, you can get this information by typing the following command:
paniclog |
If the target machine is not in a panicked state, you can obtain a list of KEXT load addresses by typing one of two commands. If you are running Mac OS X v10.4 or earlier, use the following command:
showallkmods |
Otherwise, use the following command:
showallkexts |
The debugger will then display a list of KEXTs and their load addresses.
Once you have a list of KEXT load addresses, you must create symbol files for the relevant extension(s). For each KEXT that you care about, issue the following command in a new terminal window on the development machine:
sudo kextload -n -k /Volumes/KernelDebugKit/mach_kernel -s /tmp /path/to/the/desired.kext |
After you have generated symbols for all relevant KEXTs, you can continue following the steps in this tutorial beginning with the section “On Development Machine, Import the Symbol File,” skipping any steps you have already performed in this section.
Congratulations! You've now learned how to set up a two-machine debugging environment to debug a driver (or another type of KEXT) using GDB.
If you're interested, you can use the man(1)
command to read the manual pages for kextload(8)
, kextstat(8)
, and kextunload(8)
. In particular, you may want to read about the many ways to use kextload to get debug symbols for all types of KEXTs, including I/O Kit drivers. For example, from a Terminal window, enter the following command:
$ man kextload |
You may also want to familiarize yourself with GDB. Further information is available in Tools Documentation.
© 2003, 2007 Apple Inc. All Rights Reserved. (Last updated: 2007-10-31)