
This note outlines how you can browse the internals of the Linux
kernel binary on Ubuntu or Debian. This is less fun than running "mdb
-k" on Illumos and seeing the contents of kernel memory live, but at
least you can view the linux binary image statically, at the same
addresses as the running kernel would occupy, and with co-ordinated
symbols and disassembly. (Actual addresses in your kernel may be
slightly different due to later patches; to have them exactly the same
you'll need to build your kernel yourself).

Note that the Linux kernel code makes very heavy use of macros.
Often, it's easier to see what how some code ended up compiling than
chasing these macros through several layers of expansion to their
definitions.

Ubuntu and Debian have packages for stock kernel sources and compiled
kernel image with symbols. The kernel you normally boot also comes
with symbols (look in /boot/ for vmlinuz-* and System.map-*, and grep
System.map for addresses of some known symbols!), but it stored
compressed (hence "z" in "vmlinuz"), and gdb will not like that.

My examples are from my Debian machine, running the generic stock
kernel.  You can always get the version of your stock kernel into your
bash commands with `uname -r` (in bash, backticks immediately paste
the output of the command between them into the command line).

sergey@debian:~$ uname -a
Linux debian 3.2.0-4-amd64 #1 SMP Debian 3.2.63-2+deb7u1 x86_64 GNU/Linux
sergey@debian:~$ uname -r
3.2.0-4-amd64

Instead of my version in all of your commands you can use `uname -r` for yours.
NOTE: On Ubuntu, package names vary slightly, so it's a good idea to check
with "apt-cache search linux-image | grep dbg" and similar.

Find and download the uncompressed kernel version with symbols:

sergey@debian:~$ apt-cache search linux-image-`uname -r`-dbg
linux-image-3.2.0-4-amd64-dbg - Debugging symbols for Linux 3.2.0-4-amd64

sergey@debian:~$ sudo apt-get install linux-image-3.2.0-4-amd64-dbg
<downloaded and installed>

Find where it ended up on your system:
sergey@debian:~$ dpkg-query -L linux-image-3.2.0-4-amd64-dbg | grep vmlinux
/usr/lib/debug/boot/vmlinux-3.2.0-4-amd64
/usr/lib/debug/lib/modules/3.2.0-4-amd64/vmlinux

Let's see how much space it takes & compare it with the actual kernel you boot:
sergey@debian:~$ ls -lh /usr/lib/debug/boot/vmlinux-3.2.0-4-amd64
-rw-r--r-- 1 root root 96M Jan 12 17:34 /usr/lib/debug/boot/vmlinux-3.2.0-4-amd64
sergey@debian:~$ ls -lh /boot/vmlinuz-3.2.0-4-amd64 
-rw-r--r-- 1 root root 2.8M Oct 30 08:42 /boot/vmlinuz-3.2.0-4-amd64

Now we are ready to use the above image with gdb, but let's get the source
code as well.

sergey@debian:~$ apt-cache search linux-source
linux-source-3.2 - Linux kernel source for version 3.2 with Debian patches
linux-source - Linux kernel source (meta-package)
linux-source-2.6 - Linux kernel source (dummy package)
sergey@debian:~$ sudo apt-get install linux-source-3.2
<skipped>

So, what did we just download?
sergey@debian:~$ dpkg-query -L linux-source-3.2
/.
/usr
/usr/src
/usr/src/linux-patch-3.2-rt.patch.bz2
/usr/src/linux-source-3.2.tar.bz2
/usr/share
/usr/share/doc
/usr/share/doc/linux-source-3.2
/usr/share/doc/linux-source-3.2/copyright
/usr/share/doc/linux-source-3.2/changelog.Debian.gz
/usr/share/doc/linux-source-3.2/README.Debian

So we got the Linux source archive, with Debian-specific patches. It's up
to us to un-archive it in /usr/src/

# cd /usr/src
# tar xfj /usr/src/linux-source-3.2.tar.bz2

I usually use tar with "v" option ("tar xvfj smth.tar.bz2" or "tar
xvfz smth.tar.gz") to see the files being extracted, but for the kernel
that list is huge, some 57000+ files, it considerably slows unzipping!

So now we can use both the vmlinux-* and the sources together. In fact,
one more step is needed, as I discovered:

sergey@debian:~$ gdb /usr/lib/debug/boot/vmlinux-3.2.0-4-amd64 
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
<...skipped>
Reading symbols from /usr/lib/debug/boot/vmlinux-3.2.0-4-amd64...done.
(gdb) list sys_open
999   /build/linux-p4iNsg/linux-3.2.65/fs/open.c: No such file or directory.
(gdb) quit

So I created a symlink to my source directory tree:

# mkdir /build
# mkdir /build/linux-p4iNsg
# cd /build/linux-p4iNsg
# ln -s /usr/src/linux-3.2.65 .

Restart gdb, and now we have:

sergey@debian:~$ gdb /usr/lib/debug/boot/vmlinux-3.2.0-4-amd64 
GNU gdb (GDB) 7.4.1-debian
<...skipped>
Reading symbols from /usr/lib/debug/boot/vmlinux-3.2.0-4-amd64...done.
(gdb) list sys_open
warning: Source file is more recent than executable.
999      SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
1000     {
1001            long ret;
1002		
1003            if (force_o_largefile())
1004                    flags |= O_LARGEFILE;
1005	
1006            ret = do_sys_open(AT_FDCWD, filename, flags, mode);
1007            /* avoid REGPARM breakage on x86: */
1008            asmlinkage_protect(3, ret, filename, flags, mode);

And now let's see disassembly with the corresponding code:

(gdb) disas /m sys_open
Dump of assembler code for function sys_open:
1000 {

1001            long ret;
1002	
1003            if (force_o_largefile())
1004                    flags |= O_LARGEFILE;
   0xffffffff810faa54 <+0>:   or     $0x8000,%esi

1005		      
1006            ret = do_sys_open(AT_FDCWD, filename, flags, mode);
   0xffffffff810faa5a <+6>:    mov    %edx,%ecx
   0xffffffff810faa5c <+8>:    mov    %esi,%edx
   0xffffffff810faa5e <+10>:   mov    %rdi,%rsi
   0xffffffff810faa61 <+13>:   mov    $0xffffff9c,%edi
   0xffffffff810faa66 <+18>:   jmpq   0xffffffff810fa96f <do_sys_open>

End of assembler dump.

Note that you can now see exactly how specific lines of code are compiled.
In particular, note the amd64 calling convention in the call to do_sys_open:
arguments are passed in registers, not on stack as in the 32bit ABI.

You can use "disas /r" if you'd like to see exactly how the instructions
are encoded. In particular, you can see the negative offsets (not actual 
computed addresses the disassembler is helpfully showing) in jmp-s and calls.


For a more exciting adventure with reading kernel code, look at the
"disas /m __switch_to", which is the scheduler function that switches
from one process context (virtual memory translation table and all)
to another. This switching is described in detail in pp. 105--110 of 
"Understanding the Linux Kernel" book by Bovet & Cesati, 
(http://www.amazon.com/Understanding-Linux-Kernel-Third-Edition/dp/0596005652)
and a more condensed explanation can be found in 
http://stackoverflow.com/questions/6525905/how-does-scheduleswitch-to-functions-from-linux-kernel-actually-work






