Kernel Development & Debugging Using the Eclipse IDE

7
19828
Kernel development and debugging using Eclipse

Kernel development and debugging using Eclipse

This article is targeted at Linux newbies, kernel developers, and those who are new to Eclipse. It deals with development, building and debugging of the Linux kernel using the Eclipse IDE.

Eclipse is an open source community, whose projects are focused on building an extensible development platform, runtimes, and application frameworks for building, deploying and managing software across the entire software lifecycle. Many people know (and hopefully love) it as a Java IDE, but many say that Eclipse is much more than just a Java IDE.

For this article, I have used a configuration of Ubuntu 9.10 (32-bit) running on a system with a Core 2 Duo CPU and 2 GB of RAM.

Eclipse CDT installation

Download Eclipse CDT. Extract the tar ball with the following command:

$ tar -zxvf eclipse-cpp-ganymede-SR2-linux-gtk.tar.gz

Change the working directory in your terminal window to the extracted eclipse folder, and start Eclipse, as follows:

$ cd eclipse
$ ./elipse

Building and debugging a simple C application

Once you have the Eclipse IDE running, you can start with a small C application. Select File –> New –> C Project. Give the project a name (say, “Hello”, for this article), and specify the location (keep the location field at its default value). Under Project Type, select Executable. In the submenu, select “Hello World ANSI C Project”.

Click on Next, and you are prompted with a window for some more details about the project. Fill in the appropriate data, and click Next.

The next window, for the debug/release configuration, should be answered depending on your requirement. Click Finish to conclude this step.

Next, Eclipse will ask you whether to open the C/C++ perspective or not; select Yes here. You will find “Project Explorer” on the left side on the IDE. Expand the “Hello” directory here, and you will find the “src” directory; expand this directory too.

Double-click the hello.c file in the src tree to open it. It’s a simple program, as you will see.

Now, it’s time to compile the project. Right-click the project in the Project Explorer dialogue box, and click Build Project. At the bottom, you will find a tab bar with options like Problems, Tasks, Console, etc. The Console tab displays compilation messages such as the following:

**** Build of configuration Debug for project hello ****

make all 
Building target: hello
Invoking: GCC C Linker
gcc -o"hello" ./src/hello.o  
Finished building target: hello

A binary output file named hello is created. To run it, again right-click the project, and choose Run As –> Local C/C++ Application. You will find the output of running the application in the Console tab.

Compiling the Linux kernel on Eclipse

Now let’s get down to the real work, kernel compilation. Download a kernel. I have tried this with linux-2.6.34.tar.bz2. Save it to the folder in which you want to store this project. For example:

$ mv linux-2.6.34.tar.bz2 /usr/src

Extract the kernel source and change into the extracted folder:

$ tar -jxvf linux-2.6.34.tar.bz2
$ cd linux-2.6.34

Configure your kernel with your preferred option from config/menuconfig/xconfig/gconfig.

$ make menuconfig

Visit the Kernel Hacking section; select Kernel Debugging; check “Compile the kernel with debug info” and “Compile the kernel with frame pointers”. Then, save your configuration and start Eclipse.

Before stepping into kernel compilation, you must disable automatic building and indexing, just to save time. Go to Window –> Preference –> General –> Workspace and disable the option “Build automatically”. To disable indexing, visit Window –> Preference –> General –> C/C++ –> Indexer. Select “No Indexer”.

To proceed, select File –> New –> C Project. Give a name to your project, uncheck “Use Default Location” and browse to your kernel source-code directory. From Project Type, select Makefile Project –> Empty Project. If you want to cross-compile the kernel, and you already have some other tool chain installed, you can choose it from “Toolchain”. Click Finish to complete this initial step.

Repeat the same steps to build the new project, which we have covered earlier. In the Console tab, you will see the following messages:

**** Build of configuration Linux GCC for project KernelLinux ****
make all
CHK
  include/linux/version.h
CHK
  include/generated/utsrelease.h
CALL scripts/checksyscalls.sh
CHK
  include/generated/compile.h

Now wait for a few minutes — feel free to make yourself some some coffee.

Once the compilation is over, check whether the kernel image file is created successfully:

$ ls -l /usr/src/linux-2.6.34/arch/x86/boot/bzImage
-rw-r--r-- 1 manoj src 3589920 2010-11-30 12:51 /usr/src/linux-2.6.34/arch/x86/boot/bzImage

QEMU installation and testing a kernel image (bzImage)

To debug a program, it must be run. As we are already running Ubuntu’s kernel, we have to run the newly compiled kernel in an emulator. For this purpose, we use QEMU, which is a generic open source machine emulator. The good thing about QEMU is that it can boot the Linux kernel directly. Let’s install the package. For my setup, I used the following command:

$ sudo apt-get install qemu

Once QEMU is installed, we can boot the newly compiled kernel, as follows:

$ qemu -kernel /usr/src/linux-2.6.34/arch/x86/boot/bzImage

The kernel will start booting, and will run into trouble when it tries to open the root device. We haven’t provided a root file system to QEMU, so it’s obvious that this will occur. However, now we know that Eclipse and QEMU are both working as desired. We will write a faulty module for our experiment, and will compile it as part of the kernel itself.

Open your kernel project in Eclipse. In Project Explorer, right-click the net directory; choose New –> Folder; and give it a name (“hello”, for this article). Create two files inside the “hello” directory: hello.c and Makefile, as follows:

********************* hello.c ************************
#include<linux/module.h>
#include<linux/version.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/string.h>

int hello_init(void)
{
    printk("Module named Hello inserted\n ");
    strcpy(NULL,"Hello");
    return 0;
}

void hello_exit(void)
{
    printk("Module named Hello removed\n ");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");
********************* Makefile *********************
obj-y    := hello.o
all:
    make -C ../../include M=$(PWD) modules

The resultant loadable kernel module is named hello.c.

Most kernel newbies start kernel hacking by reading O’Reilly’s Linux Device Driver book, so they can understand this code easily, but we will compile the hello module as part of the kernel. Edit the Makefile linux-2.6.34/net/Makefile, and append the following line, so that make can execute the Makefile in the hello directory:

obj-y                   +=hello/

Save the changes, and build your kernel again.

If the kvm module is installed on your system, then you must remove it from memory before launching QEMU, since this module might create problems when you set breakpoints in the kernel:

$ sudo rmmod kvm_intel
$ sudo rmmod kvm

You can boot the kernel with QEMU with this command-line (but don’t do it yet):

$ qemu -s -S /dev/zero -kernel /usr/src/linux-2.6.34/arch/x86/boot/bzImage

(-S is used to stop the emulator processor at startup. The option -gdb -p xxxx can be replaced with -s, but in this case the port number 1234 will be used as default.)

We need to specify a root filesystem. For this reason we can use /dev/zero as a dummy filesystem.

Here our concern is to debug the kernel during booting. If required, a working filesystem can be used in place of /dev/zero. Basically the filesystem is required when the kernel is booted properly. So we can debug the kernel while it’s booting.

I suggest you read the man pages of QEMU for more information. For now, we can go with these three options. The -s option will open a gdbserver on TCP port 1234 for QEMU. You can change the default port number, and the option is -gdb -p xxxx (port number). For example:

$ qemu -gdb -p 4567 -S /dev/zero -kernel /usr/src/linux-2.6.34/arch/x86/boot/bzImage

For the confirmation, execute the following command (you should see the mentioned sample results):

$ netstat -tap | grep qemu
tcp        0      0 *:4567      *:*       LISTEN      23184/qemu

You can see that QEMU is running, and it’s listening at Port Number 4567.

Go to the kernel source code directory, where you will find a file named vmlinux. This is the kernel image which stores the debugging symbols. To debug our kernel running on QEMU, we will use the following file:

$ cd /usr/src/linux-2.6.34
$ gdb ./vmlinux 
GNU gdb (GDB) 7.0-ubuntu

Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
<http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /usr/src/linux-2.6.34/vmlinux...done.
(gdb)

Important: We need to set the automatic usage of hardware breakpoints:

(gdb) set breakpoint auto-hw

Set a break point at start_kernel():

(gdb) break start_kernel
Breakpoint 1 at 0xc06ed629: file init/main.c, line 533.

Now continue the kernel booting. The kernel will stop at start_kernel().

(gdb) c
Continuing.
(gdb) n
533        smp_setup_processor_id();
(gdb) n
545        boot_init_stack_canary();
(gdb) n
547        cgroup_init_early();
(gdb) n
549        local_irq_disable();
(gdb) list
544         */
545        boot_init_stack_canary();
546    
547        cgroup_init_early();
548    
549        local_irq_disable();
550        early_boot_irqs_off();
551        early_init_irq_lock_class();

You can use GDB commands to debug the kernel. For more information on GDB commands, download the GDB manual.

Kernel debugging in Eclipse

As mentioned earlier, we need hardware breakpoints to debug a kernel running on an emulator, so it’s better to enable auto hardware breakpoints in the gdbinit file. Edit the /etc/gdb/gdbinit file and append the following line:

set breakpoint auto-hw

Boot the bzImage in QEMU. Open your project in Eclipse. Click on Run –> Debug Configurations. Double-click on Local C/C++ Application; a new configuration will be created. Name your new configuration, browse your project, and in the C/C++ Application field specify the full path to the vmlinux file, such as /usr/src/linux-2.6.34/vmlinux.

Click on the Debugger tab, and select “gdbserver Debugger” from the Debugger options. Set a breakpoint in the “Stop at startup at” field, as “hello_init”. Click on the Connection tab, select the connection type to be TCP, and the port number to be 1234. (We are debugging the kernel in two different ways. Earlier, when we debugged the kernel from the command line gdb, we could change the default port number. Here, since we are debugging with Eclipse, it would be better to stick to default options.)

Apply the changes, and start debugging by clicking the Debug button. Eclipse will build the kernel again (check the messages in the Eclipse Console), and after a few minutes, a pop-up window will ask you to open the Debug perspective; select Yes here.

In QEMU, look at the kernel messages to confirm the running condition of the kernel. Our module’s source file (hello.c) will be opened in Eclipse. To open a disassembly of the code, click Windows –> Show View –> Disassembly. Press F6 to step in to the running code. Once the strcpy function is executed, the kernel will crash (strcpy tries to copy data at a NULL pointer). In QEMU, you will find the backtrace of the kernel. If you have some knowledge of assembly coding, it is an advantage during debugging.

7 COMMENTS

  1. On my machine, I ran qemu using ‘qemu-system-x86_64’. I left the default port for gdb on the target as 1234 by utilizing the -s. On my “host”, I had to connect to the qemu machine using ‘target remote:1234’

  2. Very useful article. One small problem I had is that when I run gdb I get the complaint “The program is not being run.” If I say “run” it crashes, with no output in the qemu window. Also there should be no space between the -p and the port number

  3. Very useful info. A short question. My eclipse Kepler, but I cannot find
    Run –> Debug Configurations-> Local C/C++ Application.

    What I have are following:
    C/C++ Application
    C/C++ Attach to Application
    C/C++ Postmortem Debugging
    C/C++ Remote Application
    C/C++ Unit
    Launch Group

    As I choose C/C++ Application and then click on the Debugger tab, there is no Debugger options (i.e. “gdbserver Debugger” )? Thanks for help!

  4. I was following the steps but after adding the hello.c and Makefile and changing the makefile with new entry for hello.o, errors are coming while building the project.
    Please help.

LEAVE A REPLY

Please enter your comment!
Please enter your name here