[小知識] gnu.linkonce.xxxx

同事看了我之前的ld腳本,問這個*(.gnu.linkonce.t.*) 是幹啥的,可以拿掉嗎?

說實話只知道這類輸入段在我們系統中是沒用的,拿掉沒問題,但具體講不明白其功能作用。

我們的soc使用Freertos,這是前提。然後去網上搜了一下相關資料,這裏做個記錄。

 

https://www.oreilly.com/library/view/mastering-assembly-programming/9781787287488/0cebebcf-1089-4933-ba0e-9078b154c156.xhtml

.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,在啓動時,一起 裝載所有模塊。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章