Linux kernel run qemu

Building a Custom Linux Kernel & Debugging via QEMU + GDB

When doing systems research, we sometimes need to modify/add new stuff into the Linux kernel. This post lists a successful workflow of building and installing a custom Linux kernel under a Ubuntu 18.04/20.04 environment (deb), along with steps to debug the Linux kernel by running it over the QEMU emulator and attaching to GDB.

System Requirements & Preparations

This workflow has been tested on x86_64 arch, Ubuntu 18.04/20.04 LTS, with Linux kernel versions 4.1 — 5.15.

First, install the required dependencies (common things shipped with Ubuntu are not listed here):

sudo apt update sudo apt upgrade sudo apt install libncurses-dev flex bison openssl libssl-dev \ dkms libelf-dev libudev-dev libpci-dev \ libiberty-dev autoconf sudo apt autoremove 

Then, get the Linux source of desired version from one of the official mirror sites 1 . Untar and apply the modifications/additions you need to the code.

Building & Installing a Custom Linux Kernel

This section shows how to compile a custom Linux kernel, and how to install and boot into that kernel under a Ubuntu environment.

Compiling a Custom Kernel

To compile the custom kernel, produce the config file by:

cd linux-v.x.y # The root folder of the Linux source. make menuconfig # Tweak options & save the config to default name; # If you are later going to run with QEMU, make sure to # read the paragraphs below. 

A graphical menu should now pop up in the terminal. Tweak any options you need (e.g., turning off KPTI, KASLR, …).

If you are later going to run & debug with QEMU, these options must be selected as built-in:

  • Device drivers \(\rightarrow\) Network device support \(\rightarrow\) Virtio network driver
  • Device drivers \(\rightarrow\) Block devices \(\rightarrow\) Virtio block driver
  1. < >: not selected — will not be built
  2. : selected as built-in — will be built within the monolithic kernel
  3. : selected as a kernel module — will be built as a loadable kernel module instead of bulit-in; this is useful when you don’t want the feature, e.g. a device driver, to bloat the kernel, but want it to be loadable after booting up whenever needed

If you are later going to play with custom kernel modules, these changes will also be necessary/helpful:

  • Binary Emulations \(\rightarrow\) x32 ABI for 64-bit mode, turn this OFF [ ]
  • Enable loadable modules support \(\rightarrow\) Module unloading — Forced module unloading [*]
Читайте также:  Смена владельца группы linux

Save the tweaked config to default location .config under the source folder.

To build the kernel into an installable deb package, follow these steps:

make clean rm -rf debian rm -f vmlinux-gdb.py make -j$(nproc) LOCALVERSION=-some-suffix KDEB_PKGVERSION=1.some-suffix deb-pkg # LOCALVERSION is the suffix to be concatenated to upstream kernel version # KDEB_PKGVERSION is the suffix for the compiled .deb package 

This will take quite a while to build (~ 20-60 minutes). After successful compilation, you will find several .deb packages in the upper level folder, i.e., the folder that contains the Linux source root folder.

  • Cryptographic API \(\rightarrow\) Certificates for signature checking \(\rightarrow\) Provide system-wide ring of trusted keys, change the additional key string in the line below to EMPTY

Installing the Kernel Image

List the current list of Linux images on the machine:

Make sure that there is no image with conflicting version-suffix with the one you are going to install. If you do, first uninstall them by:

sudo apt purge linux-image-v.x.y-suffix sudo apt purge linux-image-v.x.y-suffix-dbg 

Make sure you always have at least one workable kernel available — DO NOT remove all of them before installing the new one, otherwise an unsuccessful installation might become a disaster.

Install the newly compiled kernel package by:

Booting into the Newly Installed Kernel

After successful installation, GRUB should have been updated to reflect the new kernel image in its menu. You could list the GRUB menu textually by:

awk -F\' '$1=="menuentry " || $1=="submenu " ; /\tmenuentry / "j++ " : " $2>;' /boot/grub/grub.cfg 

The desired Ubuntu subversion will have an index like 1>3 . Change GRUB config to boot into the entry by default next time:

sudo vim /etc/default/grub # Change the line to e.g. GRUB_DEFAULT="1>3" # If you need to add any boot-time command-line parameters, # do so by appending to the variable GRUB_CMDLINE_LINUX. sudo update-grub && sudo update-grub2 

Reboot and you should automatically enter the Ubuntu subversion with the custom kernel.

Debugging Linux Kernel with QEMU + GDB

This section shows how to debug the Linux kernel via running it over QEMU and attaching QEMU to GDB.

Before moving forward, install QEMU & libvirt (and GDB if it does not come along) with:

sudo apt install qemu qemu-system qemu-kvm libvirt-daemon-system \ libvirt-clients bridge-utils sudo apt install gdb 

Compiling the Kernel with Debugging Info

Be sure that the option CONFIG_GDB_SCRIPTS is ON and the option CONFIG_DEBUG_INFO_REDUCED is OFF when building the kernel. This should be the default case for recent Linux versions.

Assume we want to run the kernel on a QEMU guest with the same emulated microarchitecture, then we don’t need cross compilation. The kernel we built in sections above should already be runnable on the QEMU guest. You can find the following files under the Linux source folder:

linux-v.x.y/arch/x86_64/boot/bzImage # kernel binary image linux-v.x.y/vmlinux # target for GDB linux-v.x.y/vmlinux-gdb.py # pre-defined GDB helpers 

Add the script file to GDB’s auto load path so that you can later use the lx-* helper commands. Some descriptions about the commands can be found here 2 .

echo "add-auto-load-safe-path path/to/linux-v.x.y/vmlinux-gdb.py" >> ~/.gdbinit 

Creating the Root Filesystem

We will need a root filesystem for the kernel to boot on a QEMU guest. The buildroot project can help us on this. Details about this project can be found at https://buildroot.org 3 .

Читайте также:  Arch linux qemu guest agent

Clone the buildroot project to the same level with the Linux source folder:

git clone git://git.buildroot.net/buildroot 

Then, create an Ext4 root filesystem by:

cd buildroot make menuconfig # Please see the paragraphs below for required options. 

The required options you need to set are:

  • Target options \(\rightarrow\) Target architecture, select x86_64
  • Toolchain \(\rightarrow\) Enable C++ support [*]
  • Filesystem images \(\rightarrow\) ext2/3/4 root filesystem; then choose the ext4 variant
  • Target packages \(\rightarrow\) Network applications \(\rightarrow\) openssh [*] ; this helps us to later send files into the QEMU guest through SSH conveniently

Save the config to its default location .config , then do:

After successful compilation, you will find the root filesystem image at output/images/rootfs.ext4 .

Running on QEMU & Attaching GDB

Start QEMU on the compiled kernel with:

sudo qemu-system-x86_64 \ -kernel linux-v.x.y/arch/x86_64/boot/bzImage \ -nographic \ -drive format=raw,file=buildroot/output/images/rootfs.ext4,if=virtio \ -append "root=/dev/vda console=ttyS0 nokaslr other-paras-here-if-needed" \ -m 4G \ -enable-kvm \ -cpu host \ -smp $(nproc) \ -net nic,model=virtio \ -net user,hostfwd=tcp::10022-:22 \ -s -S 

Please refer to the QEMU documentation 4 for what these command options stand for. Notice that the nokaslr boot parameter is required, since gdb cannot work well with KASLR turned on.

Specifically, using the -s -S combo holds QEMU from booting the kernel until a GDB instance is attached. Hence, in a separate shell window, start GDB and attach to QEMU, then start breaking & debugging the Linux kernel:

sudo gdb linux-v.x.y/vmlinux (gdb) target remote :1234 # Attach to QEMU (gdb) hbreak start_kernel (gdb) b mm_alloc (gdb) c (gdb) lx-dmesg # Display kernel dmesg log in GDB shell (gdb) . 

Notice that GDB is an interactive debugger, so when we say continue, the QEMU window will continue its execution just like an OS running normally, until it hits the next break point or whatever. The interactive nature of GDB truly makes kernel debugging enjoyable.

To exit out of the QEMU nographics mode, in the QEMU window, type ctrl + a , release, then type x .

Sending Files to the QEMU Guest

Recall that we selected CONFIG_VIRTIO_NET and CONFIG_VIRTIO_BLK as built-in when building the kernel and we activated the OpenSSH package when building the rootfs. With the forwarding of host port 10022 to guest port 22 in our QEMU command line arguments, we will be able to ssh and scp from host to guest.

In the QEMU guest, after logging in, check that there is an eth0 network interface available:

Add eth0 to the list of interfaces and enable this interface by:

(in-guest) echo "iface eth0 inet dhcp" >> /etc/network/interfaces (in-guest) ifup eth0 # You might need this step every time you boot the guest 

Allow OpenSSH to accept logins as root and empty password:

(in-guest) vi /etc/ssh/sshd_config # Uncomment "PermitRootLogin" and set "yes"; # Uncomment "PermitEmptyPasswords" and set "yes" (in-guest) /etc/init.d/S50sshd restart 

Now, we should be able to ssh into the guest right from host through:

ssh -p 10022 root@localhost # Lowercase `p` for ssh 

, and send files (the most useful being compiled executables we wanna run on the debugged kernel) to the guest through:

scp -P 10022 file root@localhost:/root/some/path # Notice the uppercase `P` for scp 

Since our root filesystem is minimal, to send an executable into the guest to run, the executable must be statically linked with -static gcc option. Verify it is static by:

ldd executable_file # Should say not a dynamically linked object 

References

Please comment below anything you wanna say! 😉

Читайте также:  Cat less more linux

Источник

Use QEMU to Play with Linux Kernel

QEMU is a generic machine emulator based on dymamic translation [3]. QEMU can operates in two different modes [2]:

  • Full system emulation: This mode completely emulate a computer that can be used to launch different Operating Systems.
  • User mode emulation: Enable to launch a processes for one sort of CPU on another CPU.

If you have recent x86 machine, you can also execute QEMU with kvm which increases the speed. Finally, we decided to use QEMU since it is a very popular open source tool and it provides a many resources for easily play with virtual machine.

Install QEMU and KVM

Install QEMU and Samba package. We want the samba package for allowing the directory share.

Arch

Install Debian packages [1]:

sudo pacman -S qemu samba qemu-arch-extra 

Debian

Install Debian packages [2]:

sudo apt install qemu samba samba-client 

Create an image

In order to use Qemu to install and use a Linux Distribution, you have to create a image disk. For illustration, we create an image named kernel_experiments with 15G:

qemu-img create -f raw kernel_experiments 15G 
  • qemu-img : It is the disk image utility.
    • create : Create new disk image;
    • -f format : Specifies the format of the image. Normally, you want raw or qcow2; we discuss this in more details in this section;
    • filename : The command expects a filename to the new image. Here, we decided by kernel_experiments;
    • size : The image size, we decided by 15G.

    Installing an Distro

    Next, download the distribution ISO of your preference (I recommend Debian and Arch). With your qemu image and your distro ISO, proceed with the command:

    qemu-system-x86_64 -cdrom ~/path/distro_iso.iso -boot order=d -drive \ file=kernel_experiments,format=raw -m 2G 

    Configure ssh

    Start Qemu

    Finally, after the installation it is time to start the machine with the command:

    qemu-system-x86_64 -enable-kvm -net nic -net user,hostfwd=tcp::2222-:22,smb=$PWD/ \ -daemonize -m 2G -smp cores=4,cpus=4 kernel_experiments 

    References

    Источник

Оцените статью
Adblock
detector