|
Kernel Hacking with UML
Configuration and Compilation
|
You'll need to build UML from source, so keep in mind that you'll need
to enable
- CONFIG_DEBUG_INFO enables "-g"
- CONFIG_FRAME_POINTER enables frame pointers, which gives
you better local variable information
and head over to the
compilation instructions.
You run gdb on UML just like any other process:
% gdb obj/linux
GNU gdb Red Hat Linux (6.3.0.0-1.122rh)
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 "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) b start_kernel
Breakpoint 1 at 0x80493c3: file /home/jdike/linux/2.6/test/linux-2.6.17/init/main.c, line 461.
(gdb) r
Starting program: /home/jdike/linux/2.6/test/linux-2.6.17/obj/linux
Reading symbols from shared object read from target memory...done.
Loaded system supplied DSO at 0x615000
Detaching after fork from child process 24108.
Checking that ptrace can change system call numbers...OK
Detaching after fork from child process 24109.
Checking syscall emulation patch for ptrace...OK
Detaching after fork from child process 24110.
Checking advanced syscall emulation patch for ptrace...OK
Checking for tmpfs mount on /dev/shm...OK
Checking PROT_EXEC mmap in /dev/shm/...OK
Checking for the skas3 patch in the host:
- /proc/mm...not found
Detaching after fork from child process 24111.
- PTRACE_FAULTINFO...not found
Detaching after fork from child process 24112.
- PTRACE_LDT...not found
UML running in SKAS0 mode
Adding 25427968 bytes to physical memory to account for exec-shield gap
Breakpoint 1, start_kernel ()
at /home/jdike/linux/2.6/test/linux-2.6.17/init/main.c:461
461 smp_setup_processor_id();
(gdb) n
469 local_irq_disable();
(gdb)
Sometimes you can set a breakpoint and UML will run merrily right
through it. It seems to be a gdb problem. I have two thing I do when
this happens:
After building UML, you'll need to build the modules and install them
in a directory of your choosing:
make modules_install INSTALL_MOD_PATH=mods ARCH=um
If you have been using "O=", you'll need that here as well.
When that's done, you'll have a whole lot of stuff under "mods" (which
will be in the directory you specified with O=, if you use it). The
next step is to get the modules into UML:
-
Make the modules directory inside UML
UML# mkdir -p /lib/modules/`uname -r`
- With the network running, copy the modules over
host% scp -r mods/lib/modules/2.6.18-rc3-mm2/kernel/
root@UML:/lib/modules/2.6.18-rc3-mm2
Note that you shouldn't use `uname -r` here because that will expand
into the host's kernel version.
- Inside the UML, run depmod
UML# depmod -a
-
Now you can modprobe and insmod
UML# modprobe loop
UML# lsmod
Module Size Used by
loop 12328 0
In order to debug a module, you need to tell gdb to load the symbols
from the module and where in memory the module has been loaded.
-
Find the module structure for the module you want to debug
(gdb) p modules
$1 = {next = 0x3502cea4, prev = 0x3502cea4}
The "next" address is 4 bytes (on x86, 8 on x86_64) into the module
structure, so you can print the structure by subtracting that from the
address in the modules list_head:
(gdb) p *((struct module *)0x3502cea0)
$3 = {state = MODULE_STATE_LIVE, list = {next = 0x81e0dec, prev = 0x81e0dec},
name = "loop", '\0' <repeats 55 times>, mkobj = {kobj = {
k_name = 0x3502ceec "loop", name = "loop", '\0' <repeats 15 times>,
kref = {refcount = {counter = 2}}, entry = {next = 0x81e0ac8,
prev = 0x34a486c8}, parent = 0x81e0ad0, kset = 0x81e0ac0, ktype = 0x0,
dentry = 0x3297277c, poll = {lock = {raw_lock = {<No data fields>}},
task_list = {next = 0x3502cf1c, prev = 0x3502cf1c}}},
mod = 0x3502cea0}, param_attrs = 0x0, modinfo_attrs = 0x9598620,
version = 0x0, srcversion = 0x0, syms = 0x3502bc20, num_syms = 2,
crcs = 0x0, gpl_syms = 0x0, num_gpl_syms = 0, gpl_crcs = 0x0,
unused_syms = 0x0, num_unused_syms = 0, unused_crcs = 0x0,
unused_gpl_syms = 0x0, num_unused_gpl_syms = 0, unused_gpl_crcs = 0x0,
gpl_future_syms = 0x0, num_gpl_future_syms = 0, gpl_future_crcs = 0x0,
num_exentries = 0, extable = 0x0, init = 0x3502f000, module_init = 0x0,
module_core = 0x3502a000, init_size = 0, core_size = 12328,
init_text_size = 0, core_text_size = 6468, unwind_info = 0x0,
arch = {<No data fields>}, unsafe = 0, license_gplok = 1, ref = {{count = {
counter = 0}}}, modules_which_use_me = {next = 0x3502cfe0,
prev = 0x3502cfe0}, waiter = 0x94cf7c0, exit = 0x3502b8b6,
symtab = 0x3502bc30, num_symtab = 166, strtab = 0x3502c690 "",
sect_attrs = 0x346e14d8, percpu = 0x0, args = 0x94c3698 ""}
You can verify that offset as follow:
(gdb) p &((struct module *)0).list
$4 = (struct list_head *) 0x4
You can make sure you have the right address by looking at things like
the state and name fields and seeing that they have sane value. If
this isn't the module you want, then walk the list (the second field,
named "list"), subtracting the list field offset as before.
-
Once you find the module structure you're interested in, look at the
"module_core" field (0x3502a000 above). Use that as ADDR in this
command:
(gdb) add-symbol-file HOST_PATH_TO_KO ADDR
For example, here that command looks like this
(gdb)
obj/mods/lib/modules/2.6.18-rc3-mm2/kernel/drivers/block/loop.ko
0x3502a000
To verify that you have done this correctly, you can look at the init
function address and see that gdb agrees about its address
(gdb) p loop_init
$5 = {int (void)} 0x3502a000 <loop_init>
Now, you can set breakpoints and debug the module.
(gdb) b loop_init
Breakpoint 1 at 0x3502a009: file /home/jdike/linux/2.6/linux-2.6.18-mm/drivers/block/loop.c, line 1242.
(gdb) c
Continuing.
Breakpoint 1, loop_init ()
at /home/jdike/linux/2.6/linux-2.6.18-mm/drivers/block/loop.c:1242
1242 if (max_loop < 1 || max_loop > 256) {
(gdb)
|