Quantcast
Channel: froglogic
Viewing all articles
Browse latest Browse all 398

Code Coverage of Linux Kernel Modules

$
0
0

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:

Linux Module Build System

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 &amp;> /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.


Viewing all articles
Browse latest Browse all 398

Trending Articles