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

CLinux Kernel

C 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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJeegar PatelView Question on Stackoverflow
Solution 1 - CcnicutarView Answer on Stackoverflow
Solution 2 - CVictor ChoyView Answer on Stackoverflow
Solution 3 - CAndrew FalangaView Answer on Stackoverflow