Site Home Page
Downloads
The UML Wiki
The Old UML Site

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.
gdb
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) 
Breakpoints
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:
Modules
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) 
Hosted at SourceForge Logo