Coco can be used to evaluate the coverage of a variety of programs. Here, we showcase the instrumentation of Linux Kernel modules.
Prerequisites
This demonstration uses the Linux Kernel Version 4.19.0-9 on a Debian OS. Packages for building the kernel and for using Coco must be installed on the machine as a precondition.
The Linux Module Build System
The build system contains different steps that are necessary to build a .ko
kernel module file, displayed graphically below:

Makefile Example
ifneq ($(KERNELRELEASE),) obj-m := my_module.o my_module-y := foo.o bar.o else KDIR := /lib/modules/`uname -r`/build PWD := $(shell pwd) all: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: $(MAKE) -C $(KDIR) M=$(PWD) clean endif
Adjusting the Code
We’ve created the corresponding code for the static memory allocation and the handling of the coverage data in “coverage.h”, downloadable below. The only necessary steps are to add the following lines to your existing module source code:
#include <linux/module.h> #include <linux/init.h> #include <linux/device.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/slab.h> #ifdef __COVERAGESCANNER__ #include "coverage.h" #endif ..... static int __init my_module_init(void) { #ifdef __COVERAGESCANNER__ coverage_init(NULL, NULL); #endif ..... } ..... static void __exit my_module_exit(void) { ..... #ifdef __COVERAGESCANNER__ coverage_exit(); #endif return; } ..... module_init( my_module_init ); module_exit( my_module_exit ); .....
Making the includes Available for Coco
The linker needs some libraries which are not given when using Coco:
ln -s /usr/src/linux-headers-$(uname -r)-common/arch/x86/include/asm/ \ /usr/src/linux-headers-$(uname -r)-common/include/
ln -s /usr/src/linux-headers-$(uname -r)-common/arch/x86/include/uapi/asm/ \ /usr/src/linux-headers-$(uname -r)-common/include/uapi/
ln -s /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \ /usr/src/linux-headers-$(uname -r)-common/include/stdarg.h
Adjusting the Objtool
Since the build system temporarily changes the names of .o files in the build from .tmp_filename.o to filename.o, we need to make Coco change the names of the corresponding .csmes files as well. For this, we wrap the objtool with a small bash script. But first, we need to rename the objtool to orig_objtool for this script to work.
We place the following script in the folder /usr/lib/linux-kbuild-4.19/tools/objtool/ with the name objtool.
#! /bin/bash ## This wrapper basically takes the last command and renames the corresponding .tmp_(NAME).csmes file to the name that Coco expects LASTCOMMAND=${@: -1} ## Cutting the ".tmp_" out of the csmes file for the target B=$( echo "$LASTCOMMAND" | sed -rn 's/(.*)\.tmp_(.*)$/\1\2/p' ) B="$B.csmes" ## Renaming the csmes file mv $LASTCOMMAND.csmes $B &> /dev/null ## Normal processing D=$(dirname "$0") $D/orig_objtool $@
This script should have the x
flag. You can revert the changes by invoking:
sudo apt-get install linux-kbuild-4.19 --reinstall
to reinstall the objtool.
Adding the Correct .cspro File Switches
Add the following lines to the /opt/SquishCoco/bin/gcc.cspro:
DEACTIVATE_COVERAGESCANNER_OPTION_NO_ARG[LinuxKernel]=-M;-MM;-;-S;-dumpversion;-dumpmachine;-E;--version;-print-file-name=include
Add the following lines to the /opt/SquishCoco/bin/ld.cspro:
COMPILER_CMD[LinuxKernel]=gcc -c -O2 $LIBGEN$ -w $SOURCE$ -o $DESTINATION$ -fno-common CUSTOM_SETUP[LinuxKernel]=NONE DEACTIVATE_COVERAGESCANNER_OPTION_ONE_ARG[LinuxKernel]=--build-id LINK_ADDITIONAL_ARGUMENTS[LinuxKernel]= PLUGIN_REGISTRATION_API[LinuxKernel]=NO FILE_FORMAT_SPECIFIER[LinuxKernel]=NO
Using Coco’s Feature to Use Static Memory
Coco usually makes use of the user-space functions malloc
and free
to allocate memory for coverage data. Those calls could be replaced with the kmalloc
and kfree
Kernel variants. But for simplicity we will allocate a static memory buffer. For that purpose, we need to set the environment variable COVERAGESCANNER_ARGS.
To make it easier to set the COVERAGESCANNER_ARGS, we can use a short script, environment_variables:
#!/bin/bash export COVERAGESCANNER_ARGS="--cs-architecture=LinuxKernel --cs-memory-pool=64000 --cs-exclude=coverage.h"
Changing the Permission of the Folders
Since Coco is creating temporary files in the header folder, we need to allow Coco to write to the header folders. To do this, type the following command and change the USER spaceholder to your username:
sudo chown USER:USER -R /usr/src/linux-headers-4.19.0-9-*
Invoking make with environment_variables
source environment_variables
Now invoke make
with:
make CC=csgcc LD=csld
Now Install Your Module as Intended
insmod my_module.ko
Outputing the Coverage Data
In this showcase, the output is available via an entry in the proc directory. By running the following command, the current coverage is written to the specific file:
cat /proc/coverage/coverage_file > my_module.csexe
Now the generated my_module.o.csmes
file and the my_module.csexe
can be loaded and viewed in the CoverageBrowser or computed via the Coco command line tools.
Get coverage.h Here:
Download ‘coverage.h’Support
For any questions about Coco or this demonstration, feel free to write us an e-mail at squish@froglogic.com.
The post Code Coverage of Linux Kernel Modules appeared first on froglogic.