What's meaning of "EXPORT_SYMBOL" in Linux kernel code?
CLinux KernelC Problem Overview
from here
48 struct snd_card *snd_cards[SNDRV_CARDS];
49 EXPORT_SYMBOL(snd_cards);
I am not getting whats the meaning of it and why that is used. I tried to search about it but not understanding the meaning of that.
C Solutions
Solution 1 - C
It makes a symbol accessible to dynamically loaded modules (provided that said modules add an extern
declaration).
Not long ago, someone asked how to use it.
Solution 2 - C
Here is a good explanation.
https://www.quora.com/What-is-the-difference-between-extern-and-EXPORT_SYMBOL-in-Linux-kernel-codes
> Extern is a C storage class keyword. In the kernel, as in any other C > code, it tells the compiler that the definition of the variable or > function it qualifies is implemented in another “file”, or rather, > more accurately Translation unit (programming) - Wikipedia. The > translation unit that does define it should not use the static > qualifier. Therefore, the symbol table has an entry corresponding to > it. At link time, the symbol is resolved as normal. There is nothing > kernel specific about “extern”. > > EXPORT_SYMBOL() is a macro the Linux kernel headers define. It has not > much in common with extern. It tells the kbuild mechanism that the > symbol referred to should be part of the global list of kernel > symbols. That, in turn allows kernel modules to access them. Code that > is built into the kernel itself (as opposed to a module) can, of > course, access any non-static symbol via an extern declaration, in > accordance with regular C. The EXPORT_SYMBOL() mechanism allows us to > export a symbol for use by loadable modules as well. An interesting > thing is that a symbol thus exported by one module becomes accessible > to another module that may depend on it! > > To summarise, extern is not kernel specific. It is used to qualify a > declaration to a non-static symbol from another translation unit. > EXPORT_SYMBOL() is specific to the Linux kernel. It is used in the > translation unit of the definition to make the symbol available to > loadable modules.
So EXPORT_SYMBOL is just a mechanism like extern, but it's for reference between loadable modules not file.
To move forwards, we can guess it's achived by the extern because extern is form C which is the foundation.
Here is a clue.
https://elixir.bootlin.com/linux/v4.6.7/source/include/linux/export.h#L56
#define EXPORT_SYMBOL(sym) \
__EXPORT_SYMBOL(sym, "")
/* For every exported symbol, place a struct in the __ksymtab section */
#define __EXPORT_SYMBOL(sym, sec) \
extern typeof(sym) sym; \
__CRC_SYMBOL(sym, sec) \
static const char __kstrtab_##sym[] __attribute__((section("__ksymtab_strings"), aligned(1))) = VMLINUX_SYMBOL_STR(sym); \
extern const struct kernel_symbol __ksymtab_##sym; \
__visible const struct kernel_symbol __ksymtab_##sym __used __attribute__((section("___ksymtab" sec "+" #sym), unused)) = { (unsigned long)&sym, __kstrtab_##sym }
First declare a extern sym.
Then a string _kstrtab##sym = = VMLINUX_SYMBOL_STR(sym).
Last a extern struct kernel_symbol _ksymtab##sym = { (unsigned long)&sym, _kstrtab##sym }. &sym record the real address of the sym such as a function or variable, _kstrtab##sym record the name string.
Solution 3 - C
Not an answer per se but a demonstration, as promised from my comment, that exported symbols are not required to be non-static. The below 2 modules demonstrate this:
/* mod1.c */
#include <linux/module.h>
static int mod1_exp_func(int i)
{
pr_info("%s:%d the value passed in is %d\n",
__func__, __LINE__, i);
return i;
}
EXPORT_SYMBOL(mod1_exp_func); /* export static symbol */
static int __init mod1_init(void)
{
pr_info("Initializing simple mod\n");
return 0;
}
static void __exit mod1_exit(void)
{
pr_info("This module is exiting\n");
}
module_init(mod1_init);
module_exit(mod1_exit);
MODULE_LICENSE("GPL v2");
And the second module
/* mod2.c */
#include <linux/module.h>
extern int mod1_exp_func(int);
static int __init mod2_init(void)
{
pr_info("Initializing mod2\n");
pr_info("Calling exported function in mod1\n");
mod1_exp_func(3);
return 0;
}
static void __exit mod2_exit(void)
{
pr_info("mod2 exiting\n");
}
module_init(mod2_init);
module_exit(mod2_exit);
MODULE_LICENSE("GPL v2");
These were tested on CentOS 6 & CentOS 7: kernels 2.6.32 and 3.10 (respectively). Loading mod1.ko and then mod2.ko will result in the value passed to mod1_exp_func() being printed to the kernel log buffers.