Familiarity with your environment is crucial for productive development and debugging. This page gives a brief overview of the JOS environment and useful GDB and QEMU commands. Don’t take our word for it, though. Read the GDB and QEMU manuals. These are powerful tools that are worth knowing how to use.

Toolchain setup

You’ll use two sets of tools in this class: an x86 emulator, QEMU, for running your kernel; and a compiler toolchain, including assembler, linker, C compiler, and debugger, for compiling and testing your kernel.

If you work on attu, we have set up these tools there. Run the following command or add it to your shell’s startup file (e.g., .bashrc for bash), and you’re all set:

export PATH=/cse/courses/cse451/17sp/local/bin:$PATH

It is possible to install the toolchains on OS X (tested on 10.10 Yosemite). We provide Homebrew formulas to simplify the building process. First, install Homebrew. If you already have QEMU installed, please uninstall it first. Then, type the following commands:

$ brew tap xiw/jos
$ brew install --HEAD i386-jos-elf-qemu
$ brew install i386-jos-elf-binutils i386-jos-elf-gcc i386-jos-elf-gdb

Wait a while and you should be good to go.

If you want to build the toolchains yourself, read on.

Building QEMU

QEMU is a modern and fast PC emulator. Unfortunately, QEMU’s debugging facilities, while powerful, are somewhat immature, so we highly recommend you use our patched version of QEMU instead of the stock version that may come with your distribution.

To build your own patched version of QEMU, you may need to install the SDL development libraries to get a graphical VGA window. On Debian/Ubuntu, this is the libsdl-dev package.

$ git clone https://github.com/geofft/qemu.git -b 6.828-2.3.0
$ cd qemu
$ ./configure --target-list="i386-softmmu"
$ make && make install

If the last command fails with a statement along the lines of permission denied, or cannot find qemu, try running:

$ sudo make install

Add --prefix=PREFIX to configure if you don’t want to install QEMU to the default path, /usr/local.

Building compiler toolchain

For JOS, you’ll need a compiler toolchain that generates code for 32-bit x86 in the ELF binary format. Modern Linux/BSD x86 distributions already provide a suitable compiler toolchain. You should be all set if you use one of these distributions.

On 64-bit machines, you may need to install a 32-bit support library. The symptom is that linking fails with error messages like __udivdi3 not found and __muldi3 not found. On Debian/Ubuntu, try this to fix the problem:

$ sudo apt-get install gcc-multilib

Debugging tips

Kernel

GDB is your friend. Use make qemu-gdb (or the make qemu-gdb-nox variant) to make QEMU wait for GDB to attach. See the GDB reference below for some commands that are useful when debugging kernels.

If you’re getting unexpected interrupts, exceptions, or triple faults, you can ask QEMU to generate a detailed log of interrupts using the -d argument.

To debug virtual memory issues, try the QEMU monitor commands info mem (for a high-level overview) and info pg (for lots of detail). Note that these commands only display the current page table.

(Lab 4+) To debug multiple CPUs, use GDB’s thread-related commands like thread and info threads.

User environments

GDB also lets you debug user environments, but there are a few things you need to watch out for, since GDB doesn’t know that there’s a distinction between multiple user environments, or between user and kernel.

You can start JOS with a specific user environment using make run-name (or you can edit kern/init.c directly). To make QEMU wait for GDB to attach, use the make run-name-gdb variant.

You can symbolically debug user code, just like you can kernel code, but you have to tell GDB which symbol table to use with the symbol-file command, since it can only use one symbol table at a time. The provided .gdbinit loads the kernel symbol table, obj/kern/kernel. The symbol table for a user environment is in its ELF binary, so you can load it using symbol-file obj/user/name. Don’t load symbols from any .o files, as those haven’t been relocated by the linker (libraries are statically linked into JOS user binaries, so those symbols are already included in each user binary). Make sure you get the right user binary; library functions will be linked at different EIPs in different binaries and GDB won’t know any better!

(Lab 4+) Since GDB is attached to the virtual machine as a whole, it sees clock interrupts as just another control transfer. This makes it basically impossible to step through user code because a clock interrupt is virtually guaranteed the moment you let the VM run again. The stepi command works because it suppresses interrupts, but it only steps one assembly instruction. Breakpoints generally work, but watch out because you can hit the same EIP in a different environment (indeed, a different binary altogether!).

Reference

JOS makefile

The JOS GNUmakefile includes a number of phony targets for running JOS in various ways. All of these targets configure QEMU to listen for GDB connections (the *-gdb targets also wait for this connection). To start once QEMU is running, simply run make gdb from your lab directory. We provide a .gdbinit file that automatically points GDB at QEMU, loads the kernel symbol file, and switches between 16-bit and 32-bit mode. Exiting GDB will shut down QEMU.

GNUmakefile also accepts a few useful variables:

JOS obj/

When building JOS, the makefile produces some additional output files that may prove useful while debugging:

GDB

See the GDB manual for a full guide to GDB commands. Here are some particularly useful commands for JOS, some of which don’t typically come up outside of OS development.

QEMU represents each virtual CPU as a thread in GDB, so you can use all of GDB’s thread-related commands to view or manipulate QEMU’s virtual CPUs.

QEMU

QEMU includes a built-in monitor that can inspect and modify the machine state in useful ways. To enter the monitor, press Ctrl-a c in the terminal running QEMU. Press Ctrl-a c again to switch back to the serial console.

For a complete reference to the monitor commands, see the QEMU manual. Here are some particularly useful commands:

QEMU also takes some useful command line arguments, which can be passed into the JOS makefile using the QEMUEXTRA variable.