What's meaning of "EXPORT_SYMBOL" in Linux kernel code?

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.


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.


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.


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.