同事看了我之前的ld腳本,問這個*(.gnu.linkonce.t.*) 是幹啥的,可以拿掉嗎?
說實話只知道這類輸入段在我們系統中是沒用的,拿掉沒問題,但具體講不明白其功能作用。
我們的soc使用Freertos,這是前提。然後去網上搜了一下相關資料,這裏做個記錄。
.gnu.linkonce.this_module
There's nothing special to say here. This section contains only one structure--this_module, which is mostly filled with zeroes (as it is used by the LKM loader internally) except three fields:
- Name of the module
- A pointer to the initialization procedure--module_init
- A pointer to the de-initialization procedure--module_cleanup
首先,這是個模塊說明的結構體,用於LKM中,包括了模塊名字、初始化和卸載的指針。
搜索LKM,其全稱爲動態可加載內核模塊(Loader Kernel Module , LKM),內核模塊是Linux 內核向外提供的一個插口。
原來是Linux kernel用的東西,這個沒怎麼接觸過。
https://github.com/tleonhardt/linux_lkm/blob/master/hello_world/hello_world.c
這裏舉例hello_world模塊是如何初始化的
static char *name = "world"; // example LKM argument default is "world"
module_param(name, charp, S_IRUGO); // S_IRUGO can be read/not changed
MODULE_PARM_DESC(name, "The name to display in /var/log/kern.log");
module_init(hello_init); //hello_init是初始化函數
module_exit(hello_exit); /hello_exit是卸載函數
這些宏應該來自 #include <linux/module.h>
https://elixir.bootlin.com/linux/v5.1.9/source/include/linux/module.h
/* Each module must use one module_init(). */
#define module_init(initfn) \
static inline initcall_t __maybe_unused __inittest(void) \
{ return initfn; } \
int init_module(void) __copy(initfn) __attribute__((alias(#initfn)));
/* This is only required if you want to be unloadable. */
#define module_exit(exitfn) \
static inline exitcall_t __maybe_unused __exittest(void) \
{ return exitfn; } \
void cleanup_module(void) __copy(exitfn) __attribute__((alias(#exitfn)));
這裏又呼叫了init_module()和clearup_module();
實現在module.c中,好長的code,沒有再看下去。
https://elixir.bootlin.com/linux/v5.1.9/source/kernel/module.c
static int setup_load_info(struct load_info *info, int flags)
{
unsigned int i;
/* Set up the convenience variables */
info->sechdrs = (void *)info->hdr + info->hdr->e_shoff;
info->secstrings = (void *)info->hdr
+ info->sechdrs[info->hdr->e_shstrndx].sh_offset;
/* Try to find a name early so we can log errors with a module name */
info->index.info = find_sec(info, ".modinfo");
if (!info->index.info)
info->name = "(missing .modinfo section)";
else
info->name = get_modinfo(info, "name");
/* Find internal symbols and strings. */
for (i = 1; i < info->hdr->e_shnum; i++) {
if (info->sechdrs[i].sh_type == SHT_SYMTAB) {
info->index.sym = i;
info->index.str = info->sechdrs[i].sh_link;
info->strtab = (char *)info->hdr
+ info->sechdrs[info->index.str].sh_offset;
break;
}
}
if (info->index.sym == 0) {
pr_warn("%s: module has no symbols (stripped?)\n", info->name);
return -ENOEXEC;
}
info->index.mod = find_sec(info, ".gnu.linkonce.this_module");
if (!info->index.mod) {
pr_warn("%s: No module found in object\n",
info->name ?: "(missing .modinfo name field)");
return -ENOEXEC;
}
/* This is temporary: point mod into copy of data. */
info->mod = (void *)info->hdr + info->sechdrs[info->index.mod].sh_offset;
/*
* If we didn't load the .modinfo 'name' field earlier, fall back to
* on-disk struct mod 'name' field.
*/
if (!info->name)
info->name = info->mod->name;
if (flags & MODULE_INIT_IGNORE_MODVERSIONS)
info->index.vers = 0; /* Pretend no __versions section! */
else
info->index.vers = find_sec(info, "__versions");
info->index.pcpu = find_pcpusec(info);
return 0;
}
info->index.mod = find_sec(info, ".gnu.linkonce.this_module"); //這應該是裝載模塊時搜索
具體的還是需要下載kernel完整的查看一下才行,暫時到這裏做個標記先!
理論上可能和device open/init原理差不多,通過ld收集所有的module 結構體放在一個table中存入某個輸出section,在啓動時,一起 裝載所有模塊。