Porting C++ code to Linux kernel
April 5, 2009 at 1:01 am 1 comment
We all know writing Linux kernel module in C++ is not a good idea, but sometimes we don’t have options, especially when porting a existing project from Windows to Linux.
The main constrains are:
1) There is no C++ runtime in kernel. Most features of C++ will work, virtual functions, templates, operators, etc. But their is no default implementation of new and delete operators, so you need to write your own.
2) Stack space is very limited. In Linux kernel, default size of stack is 2 pages. That is 8k for 32-bit systems and 16k for 64-bit ones. Actually, stack is a little bit smaller than that, because the task structure for the thread also reside in these 2 pages. Due to this constrain, you can’t use exceptions in kernel, because exception needs too much space in stack. There are some open source C++ runtimes for kernel, which support exception, but I haven’t tried any of them.
Here are some tips I learned in a recent project:
1) Implement you own new and delete operators.
2) Do not use exceptions. If you are porting a Windows driver, probably you don’t have to worry about this.
3) Do not use global non-trival variables. Since there is no C++ runtime in kernel, nobody will construct those variable for you. There are better ways to do it, singleton, factory, etc.
4) Implement platform-dependent functions. Making C++ code work with kerenl headers is not a simple job. I wrote this part in C. If you know a better way, plese let me know.
5) Compile your c++ code with -fno-builtin -fno-exceptions -fno-rtti -nostdinc, and link obj files to your platform-dependent part.
Ok, that’s it. I attached a sample project. If I missed anything, hopefully the code can explain it. (Why wordpress does not allow zip files? Is there any better way to post code including several files?)
// =======================================
// new.h, implement new and delete
#ifndef NEW_H
#define NEW_H
#include “kernel_api.h”
inline void *operator new(size_t s) {
return my_alloc(s);
}
inline void operator delete(void *p) {
return my_free(p);
}
// you may need other forms of new and delete
#endif
// =========================================
// kernel_api.h.
#ifndef KERNEL_API_H
#define KERNEL_API_H
#ifdef __cplusplus
extern “C” {
#endif
typedef unsigned int size_t;
#define KERN_INFO “<6>”
#define NULL 0UL
extern void printk(const char *fmt, …);
extern void *my_alloc(size_t size);
extern void my_free(void *p);
#ifdef __cplusplus
}
#endif
#endif
//==========================================
// kernel_api.c
#include <linux/module.h>
void *my_alloc(size_t s) {
return kmalloc(s, GFP_KERNEL);
}
void my_free(void *p) {
return kfree(p);
}
//==========================================
// interface.c, required interface for every kernel module
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include “cppmod.h”
static int __init cppmod_init(void)
{
printk(KERN_INFO “cpp module installed\n”);
return 0;
}
static void __exit cppmod_exit(void)
{
printk(KERN_INFO “cpp module removed\n”);
}
module_init(cppmod_init);
module_exit(cppmod_exit);
MODULE_LICENSE(“GPL”);
//==========================================
// cppmod.h, exported C interface from C++ code
#ifndef CPP_MOD_H
#define CPP_MOD_H
#ifdef __cplusplus
extern “C” {
#endif
extern int start_driver(void* data);
extern void stop_driver(void);
#ifdef __cplusplus
}
#endif
#endif
//==========================================
// cppmod.cpp, finally, my C++ code
#include “kernel_api.h”
#include “new.h”
#include “cppmod.h”
class Driver {
public:
Driver(unsigned int dev_id) : _id(dev_id) { printk(KERN_INFO “C++ driver started\n”); }
~Driver(void) { printk(KERN_INFO “Goodbye C++ driver\n”); }
private:
unsigned int _id;
};
static Driver *g_driver = NULL;
extern “C” int start_driver(void *data) {
g_driver = new Driver(*(unsigned int*)data);
if(!g_driver)
return -1;
return 0;
}
extern “C” void stop_driver(void) {
if(g_driver)
delete g_driver;
}
#####################################################
# Makefile
obj-m += mydrv.o
mydrv-objs := interface.o cppmod.o kernel_api.o
list-multi := mydrv.o
all: cppmod.o module
module:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
cppmod.o : cppmod.cpp new.h kernel_api.h cppmod.h
$(CC) -c -o $@ -fno-builtin -fno-exceptions -fno-rtti -nostdinc $<
Entry filed under: kernel programming. Tags: C++, Linux kernel.
1. Nathan Cooprider | July 22, 2011 at 7:48 am
This example does not seem to work for me. Is this code rot or am I doing something wrong?
ncooprider@machine:~/sandbox/try3$ ls
cppmod.cpp cppmod.h interface.c kernel_api.c kernel_api.h Makefile new.h
ncooprider@machine:~/sandbox/try3$ make
cc -c -o cppmod.o -fno-builtin -fno-exceptions -fno-rtti -nostdinc cppmod.cpp
In file included from cppmod.cpp:4:
new.h:6: error: ‘operator new’ takes type ‘size_t’ (‘long unsigned int’) as first parameter
make: *** [cppmod.o] Error 1