Linux Device Driver Tutorial Part 29 – EXPORT_SYMBOL in Linux Device Driver

This is the Series on Linux Device Driver. The aim of this series is to provide easy and practical examples that anyone can understand. This is the Linux Device Driver Tutorial Part 29 – EXPORT_SYMBOL in Linux Device Driver.

Note: In this article I assume that you already know the basic routines for kernel module development.

When you are writing multiple drivers (modules) in the same device, you may wanted to use some of the functions form one module to another module. How will we do that? If we use only extern then it won’t help you. We must have use some advance thing. So, We have to tell the kernel, that I want to share this function to other modules.

For example, take printk() function. This function will be defined in source/kernel/printk/printk.c. Then how can we able to access that printk() in our driver?

In this article we will see how to do it.

EXPORT_SYMBOL in Linux Device Driver

Introduction

In programming language, a symbol is either a variable or a function. Or more generally, we can say, a symbol is a name representing an space in the memory, which stores data (variable, for reading and writing) or instructions (function, for executing).

When you look at some kernel codes, you may find EXPORT_SYMBOL() very often. Have you wondered any time what the heck is that?

In the Linux Kernel 2.4, all the non-static symbols are exported to the kernel space automatically. But later, in Linux Kernel 2.6 instead of exporting all non-static symbols, they wanted to export the only symbols which is marked by EXPORT_SYMBOL() macro.

EXPORT_SYMBOL’s role

When some symbols (variables or functions) are using EXPORT_SYMBOL macro (ex. EXPORT_SYMBOL(func_name)), those symbols are exposed to all the loadable kernel driver. You can call them directly in your kernel module without modifying the kernel code. In other words, It tells the kbuild mechanism that the symbol referred to should be part of the global list of kernel symbols. That allows the kernel modules to access them.

Only the symbols that have been explicitly exported can by used by other modules.

Another macro is also available to export the symbols like EXPORT_SYMBOL. That is EXPORT_SYMBOL_GPL().

EXPORT_SYMBOL exports the symbol to any loadable module.
EXPORT_SYMBOL_GPL exports the symbol only to GPL-licensed modules.

How to use EXPORT_SYMBOL?

  • Declare and define the symbol (functions or variables) which you want to make it visible to other kernel modules. Then below the definition, use EXPORT_SYMBOL(symbol name). Now it is visible to all loadable modules.
  • Now take the kernel driver who is gonna use the above exported symbol. Declare the symbol using extern. Then use the symbol directly.
  • Finally, load the module first, who has the definition of the export symbol. Then load the caller module using insmod“.

Limitation

  • That symbol should not be static or inline.
  • Order of loading the driver is matter. ie. We should load the module which has the definition of the symbol, then only we can load the module who is using that symbol.

Driver Source Code – EXPORT_SYMBOL in Linux

First I will explain you the concept of driver code attached below.

In this tutorial we have two drivers.

Driver 1 has one function called etx_shared_func and one global variable called etx_count. This function and variable has been shared among with all the loadable modules using EXPORT_SYMBOL.

Driver 2 will be using that variable and function which are shared by Driver 1. When we read this Driver 2, then it will call the shared function and we can read that variable also.

Let’s see the source code below.

driver1.c

driver2.c

MakeFile

Compiling and Testing Driver

  • Build the driver by using Makefile (sudo make)
  • After compiling, you can able to see the file named as “Module.symvers“. If you open that file, then our shared function and variable will be mentioned there.

0x1db7034a       etx_shared_func         /home/embetronicx/driver/driver1            EXPORT_SYMBOL
0x6dcb135c       etx_count                    /home/embetronicx/driver/driver1             EXPORT_SYMBOL

  • Load the driver 1 using sudo insmod driver1.ko(Driver 1 should be loaded first. If you try to load the Driver 2 first, then you will get an error like “insmod: ERROR: could not insert module driver2.ko: Unknown symbol in module“).
  • Load the driver 1 using sudo insmod driver2.ko
  • Now check the dmesg

[ 393.814900] Major = 246 Minor = 0
[ 393.818413] Device Driver 1 Insert…Done!!!
[ 397.620296] Major = 245 Minor = 0
[ 397.629002] Device Driver 2 Insert…Done!!!

  • Then do cat /proc/kallsyms | grep etx_shared_funcorcat /proc/kallsyms | grep etx_countto check whether our shared function and variable become the part of kernel’s symbol table or not.
  • Now we can read the driver by using sudo cat /dev/etx_device2
  • Now check the dmesg

[ 403.739998] Device File Opened…!!!
[ 403.740018] Shared function been called!!!
[ 403.740021] 1 time(s) shared function called!
[ 403.740023] Data Read : Done!
[ 403.740028] Device File Closed…!!!

  • Now we can see the print from shared function and variable count also.
  • Unload the module 2 using sudo rmmod driver2(Driver 2 should be unloaded first. If you unload the Driver 1 first, then you will get error like “rmmod: ERROR: Module driver1 is in use by: driver2“).
  • Unload the module 1 using sudo rmmod driver1
%d bloggers like this: