MODULE_LICENSE的展开和检测

我们在写驱动模块的时候,一定会包含 <linux/module.h>,在我们的驱动模块代码中,一定会写MODULE_LICENSE("GPL");
这个语句发生了什么,起到了什么作用?不写会怎么样?

在kernel/module.c 和 include/linux/module.h


#define MODULE_LICENSE(_license) MODULE_INFO(license, _license)

#define MODULE_INFO(tag, info) __MODULE_INFO(tag, tag, info)

#define __MODULE_INFO(tag, name, info)            \
static const char __UNIQUE_ID(name)[]           \
  __used __attribute__((section(".modinfo"), unused, aligned(1)))   \
  = __stringify(tag) "=" info
 
#define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __LINE__)
#define ___PASTE(a,b) a##b
#define __PASTE(a,b) ___PASTE(a,b)

#define __stringify_1(x...) #x
#define __stringify(x...) __stringify_1(x)

MODULE_LICENSE展开

  MODULE_INFO(license,"LIG")
  -->
    __MODULE_INFO(license, license, "LIG")
    -->
      static const char __UNIQUE_ID(license)[] __used __attribute__((section(".modinfo"), unused, aligned(1)))  = __stringify(license) "=" "LIG"
      -->
        static const char __PASTE(__PASTE(__UNIQUE_ID_, license), __LINE__)[] __used __attribute__((section(".modinfo"), unused, aligned(1)))  = __stringify(license) "=" "LIG"
        -->
          static const char __UNIQUE_ID_license288[] __used __attribute__((section(".modinfo"), unused, aligned(1)))  = __stringify(license) "=" "LIG"
          -->
            static const char __UNIQUE_ID_license288[] __used __attribute__((section(".modinfo"), unused, aligned(1)))  = "license" "=" "LIG"
            
//static const char __UNIQUE_ID_license288[]  定义一个静态的const char数组
//__used __attribute__((section(".modinfo"), unused, aligned(1)))  这是对这个数组的修饰
// __used 应该是防止编译器警告,我猜的
//       (section(".modinfo"),这个数组放置在".modinfo"段
//       unused表示这个变量后续程序可能不使用,但是编译器不要对其进行优化
//       aligned(1) 表示这个数组1字节对齐,
//"license" "=" "LIG"  : 这个根据C语音的字符串常量规则,会自动拼接成 "license=LIG"`

最终展开的的语句语义为: 在.modinfo段,定义一个名称为__UNIQUE_ID_license288(288是行号)的static const char[]类型常量,这个常量赋值为"license=LIG"

一个helloworld驱动模块代码:

>>> cat -n helloworld.c 
     1  #include <linux/init.h>
     2  #include <linux/module.h>
     3
     4  //程序入口
     5  static int helloworld_init(void)
     6  {
     7      printk("%s\n", __func__);
     8      return 0;
     9  }
    10
    11  //程序出口
    12  static void helloworld_exit(void)
    13  {
    14      printk("%s\n", __func__);
    15  }
    16
    17  //告诉内核入口和出口
    18  module_init(helloworld_init);
    19  module_exit(helloworld_exit);
    20
    21  //添加许可声明
    22  MODULE_LICENSE("GPL"); //是个.c都要加

查看驱动模块中的符号

objdump -s hello.ko
...

Contents of section .modinfo:
 0000 6c696365 6e73653d 47504c00 73726376  license=GPL.srcv
 0010 65727369 6f6e3d30 46373644 38433430  ersion=0F76D8C40
 0020 30373444 30444232 39363444 31300000  074D0DB2964D10..
 0030 64657065 6e64733d 00726574 706f6c69  depends=.retpoli
 0040 6e653d59 006e616d 653d6865 6c6c6f00  ne=Y.name=hello.
 0050 7665726d 61676963 3d342e31 352e302d  vermagic=4.15.0-
 0060 3133362d 67656e65 72696320 534d5020  136-generic SMP 
 0070 6d6f645f 756e6c6f 61642036 38362000  mod_unload 686 .

...

objdump -t hello.ko 
00000000 l     O .modinfo       0000000c __UNIQUE_ID_license20

可以看到里面有

6c696365 6e73653d 47504c00  license=GPL.

如果删掉 MODULE_LICENSE("GPL");

sudo insmod hello.ko 
cat /var/log/kern.log

May  8 17:54:01 alex-ubuntu-pad kernel: [33958.746580] hello: loading out-of-tree module taints kernel.
May  8 17:54:01 alex-ubuntu-pad kernel: [33958.746587] hello: module license 'unspecified' taints kernel.
May  8 17:54:01 alex-ubuntu-pad kernel: [33958.746588] Disabling lock debugging due to kernel taint
May  8 17:54:01 alex-ubuntu-pad kernel: [33958.746648] hello: module verification failed: signature and/or required key missing - tainting kernel
May  8 17:54:01 alex-ubuntu-pad kernel: [33958.748070] helloworld_init
(imx_4.1.15_2.0.0_ga)>>> grep -r "module verification failed" *            
kernel/module.c:                pr_notice_once("%s: module verification failed: signature "
SYSCALL_DEFINE3(init_module, void __user *, umod,unsigned long, len, const char __user *, uargs)
    -->static int copy_module_from_fd(int fd, struct load_info *info)
        -->
            pos = 0;
            while (pos < stat.size) {
                bytes = kernel_read(f.file, pos, (char *)(info->hdr) + pos,
                stat.size - pos);
                if (bytes < 0) {
                vfree(info->hdr);
                err = bytes;
                goto out;
            }
            if (bytes == 0)
                break;
                pos += bytes;
            }
            info->len = pos;
    -->static int load_module(struct load_info *info, const char __user *uargs, int flags)
        -->module_sig_check (struct load_info *info)
            -->mod_verify_sig(const void *mod, unsigned long *_modlen)/*这里面有一些数字签名认证*/
        --> pr_notice_once("%s: module verification failed: signature "

对于内核驱动模块,通过SYSCALL_DEFINE3进入内核,内核获取.modinfo的信息,进行判断

struct load_info {
	Elf_Ehdr *hdr;
	unsigned long len;
	Elf_Shdr *sechdrs;
	char *secstrings, *strtab;
	unsigned long symoffs, stroffs;
	struct _ddebug *debug;
	unsigned int num_debug;
	bool sig_ok;
	struct {
		unsigned int sym, str, mod, vers, info, pcpu;
	} index;
};

SYSCALL_DEFINE3是个哈?

include/linux/syscalls.h

#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)

#define SYSCALL_DEFINEx(x, sname, ...)				\
	SYSCALL_METADATA(sname, x, __VA_ARGS__)			\
	__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)

#define SYSCALL_METADATA(sname, nb, ...)			\
	static const char *types_##sname[] = {			\
		__MAP(nb,__SC_STR_TDECL,__VA_ARGS__)		\
	};							\
	static const char *args_##sname[] = {			\
		__MAP(nb,__SC_STR_ADECL,__VA_ARGS__)		\
	};							\
	SYSCALL_TRACE_ENTER_EVENT(sname);			\
	SYSCALL_TRACE_EXIT_EVENT(sname);			\
	static struct syscall_metadata __used			\
	  __syscall_meta_##sname = {				\
		.name 		= "sys"#sname,			\
		.syscall_nr	= -1,	/* Filled in at boot */	\
		.nb_args 	= nb,				\
		.types		= nb ? types_##sname : NULL,	\
		.args		= nb ? args_##sname : NULL,	\
		.enter_event	= &event_enter_##sname,		\
		.exit_event	= &event_exit_##sname,		\
		.enter_fields	= LIST_HEAD_INIT(__syscall_meta_##sname.enter_fields), \
	};							\
	static struct syscall_metadata __used			\
	  __attribute__((section("__syscalls_metadata")))	\
	 *__p_syscall_meta_##sname = &__syscall_meta_##sname;

#define __SYSCALL_DEFINEx(x, name, ...)					\
	asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))	\
		__attribute__((alias(__stringify(SyS##name))));		\
	static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__));	\
	asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__));	\
	asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__))	\
	{								\
		long ret = SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__));	\
		__MAP(x,__SC_TEST,__VA_ARGS__);				\
		__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));	\
		return ret;						\
	}								\
	static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__))

#define __PROTECT(...) asmlinkage_protect(__VA_ARGS__)

展开后

SYSCALL_DEFINE3(init_module, void __user *, umod,unsigned long, len, const char __user *, uargs)
    SYSCALL_DEFINEx(3, _init_module, void __user *, umod,unsigned long, len, const char __user *, uargs)
        SYSCALL_METADATA(_init_module, 3, void __user *, umod,unsigned long, len, const char __user *, uargs)   __SYSCALL_DEFINEx(3, _init_module, void __user *, umod,unsigned long, len, const char __user *, uargs)
          //SYSCALL_METADATA(sname, nb, ...)
          //SYSCALL_METADATA(_init_module, 3, void __user *, umod,unsigned long, len, const char __user *, uargs) 
          static const char *types__init_module[] = {
            __MAP(3,__SC_STR_TDECL,void __user *, umod,unsigned long, len, const char __user *, uargs)
          };

          static const char *args__init_module[] = {
            __MAP(3,__SC_STR_ADECL,void __user *, umod,unsigned long, len, const char __user *, uargs)
          };
          SYSCALL_TRACE_ENTER_EVENT(_init_module);
          SYSCALL_TRACE_EXIT_EVENT(_init_module);
          static struct syscall_metadata __used  __syscall_meta__init_module = {
            .name     = "sys_init_module",
            .syscall_nr = -1, /* Filled in at boot */
            .nb_args  = 3,
            .types    = 3 ? types__init_module : NULL,
            .args   = 3 ? args__init_module : NULL,
            .enter_event  = &event_enter__init_module,
            .exit_event = &event_exit__init_module,
            .enter_fields = LIST_HEAD_INIT(__syscall_meta__init_module.enter_fields),
          };
          static struct syscall_metadata __used   __attribute__((section("__syscalls_metadata")))   *__p_syscall_meta__init_module = &__syscall_meta__init_module;

          //__SYSCALL_DEFINEx(x, name, ...) 
          //__SYSCALL_DEFINEx(3, _init_module, void __user *, umod,unsigned long, len, const char __user *, uargs)
          asmlinkage long sys_init_module(__MAP(3,__SC_DECL,void __user *, umod,unsigned long, len, const char __user *, uargs))    __attribute__((alias(__stringify(SyS_init_module))));

          static inline long SYSC_init_module(__MAP(x,__SC_DECL,,void __user *, umod,unsigned long, len, const char __user *, uargs));
          asmlinkage long SyS_init_module(__MAP(x,__SC_LONG,,void __user *, umod,unsigned long, len, const char __user *, uargs));
          asmlinkage long SyS_init_module(__MAP(x,__SC_LONG,,void __user *, umod,unsigned long, len, const char __user *, uargs))
          {
            long ret = SYSC_init_module(__MAP(x,__SC_CAST,,void __user *, umod,unsigned long, len, const char __user *, uargs));  \
            __MAP(x,__SC_TEST,,void __user *, umod,unsigned long, len, const char __user *, uargs);       \
            asmlinkage_protect(x, ret,__MAP(x,__SC_ARGS,,void __user *, umod,unsigned long, len, const char __user *, uargs)); \
            return ret;           \
          }               \
          static inline long SYSC_init_module(__MAP(x,__SC_DECL,,void __user *, umod,unsigned long, len, const char __user *, uargs));

(真尼玛长) 待续

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值