• Linux内核EXPORT_SYMBOL宏解析



      本文针对ARM32处理器进行说明。使用内核4.0版本。

    宏定义展开

      EXPORT_SYMBOL宏定义于include/linux/export.h

      众所周知,EXPORT_SYMBOL宏是导出符号给模块(ko)使用的,所以当内核配置不支持动态加载ko时,EXPORT_SYMBOL宏为空

    #else /* !CONFIG_MODULES... */
    
    #define EXPORT_SYMBOL(sym)
    #define EXPORT_SYMBOL_GPL(sym)
    #define EXPORT_SYMBOL_GPL_FUTURE(sym)
    #define EXPORT_UNUSED_SYMBOL(sym)
    #define EXPORT_UNUSED_SYMBOL_GPL(sym)
    
    #endif /* CONFIG_MODULES */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

      在内核配置支持ko加载后(MODULE),宏定义展开如下:

    #define __CRC_SYMBOL(sym, sec)					\
    	extern __visible void *__crc_##sym __attribute__((weak));		\
    	static const unsigned long __kcrctab_##sym		\
    	__used							\
    	__attribute__((section("___kcrctab" sec "+" #sym), unused))	\
    	= (unsigned long) &__crc_##sym;
    #else
    #define __CRC_SYMBOL(sym, sec)
    #endif
    
    /* 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[]			\											(1)
    	__attribute__((section("__ksymtab_strings"), aligned(1))) \
    	= VMLINUX_SYMBOL_STR(sym);				\
    	extern const struct kernel_symbol __ksymtab_##sym;	\									(2)
    	__visible const struct kernel_symbol __ksymtab_##sym	\
    	__used							\
    	__attribute__((section("___ksymtab" sec "+" #sym), unused))	\							(3)
    	= { (unsigned long)&sym, __kstrtab_##sym }
    
    #define EXPORT_SYMBOL(sym)					\
    	__EXPORT_SYMBOL(sym, "")
    
    #define EXPORT_SYMBOL_GPL(sym)					\
    	__EXPORT_SYMBOL(sym, "_gpl")
    
    #define EXPORT_SYMBOL_GPL_FUTURE(sym)				\
    	__EXPORT_SYMBOL(sym, "_gpl_future")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

      __EXPORT_SYMBOL宏展开。
    1)以符号名定义字符串数组__kstrtab_##sym,链接到__ksymtab_strings section中。以1字节对齐(节省内存空间)。把函数名存放于字符串中。VMLINUX_SYMBOL_STR正常情况下返回原值。
    2)定义struct kernel_symbol数据结构

    struct kernel_symbol
    {
    	unsigned long value;
    	const char *name;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

      这个数据结构8个字节,两个成员,value指向函数指针,name指向上文中的strtab地址。insmod时,模块会寻找依赖的符号。就是从kstrtab_symbol表中遍历。从name指针获取函数名字符串,比对一致后,获取strtabvalue值(内核函数指针)。即模块需要的符号。
    3)链接到___symtab section中。

    链接脚本

      RO_DATA_SECTION宏定义于vmlinux.lds.h中。ksymtabkstrtab相关链接逻辑定义于其中

    	/* Kernel symbol table: Normal symbols */			\
    	__ksymtab         : AT(ADDR(__ksymtab) - LOAD_OFFSET) {		\
    		VMLINUX_SYMBOL(__start___ksymtab) = .;			\
    		*(SORT(___ksymtab+*))					\
    		VMLINUX_SYMBOL(__stop___ksymtab) = .;			\
    	}								\
    									\
    	/* Kernel symbol table: GPL-only symbols */			\
    	__ksymtab_gpl     : AT(ADDR(__ksymtab_gpl) - LOAD_OFFSET) {	\
    		VMLINUX_SYMBOL(__start___ksymtab_gpl) = .;		\
    		*(SORT(___ksymtab_gpl+*))				\
    		VMLINUX_SYMBOL(__stop___ksymtab_gpl) = .;		\
    	}								\
    									\
    	/* Kernel symbol table: Normal unused symbols */		\
    	__ksymtab_unused  : AT(ADDR(__ksymtab_unused) - LOAD_OFFSET) {	\
    		VMLINUX_SYMBOL(__start___ksymtab_unused) = .;		\
    		*(SORT(___ksymtab_unused+*))				\
    		VMLINUX_SYMBOL(__stop___ksymtab_unused) = .;		\
    	}								\
    									\
    	/* Kernel symbol table: GPL-only unused symbols */		\
    	__ksymtab_unused_gpl : AT(ADDR(__ksymtab_unused_gpl) - LOAD_OFFSET) { \
    		VMLINUX_SYMBOL(__start___ksymtab_unused_gpl) = .;	\
    		*(SORT(___ksymtab_unused_gpl+*))			\
    		VMLINUX_SYMBOL(__stop___ksymtab_unused_gpl) = .;	\
    	}								\
    									\
    	/* Kernel symbol table: GPL-future-only symbols */		\
    	__ksymtab_gpl_future : AT(ADDR(__ksymtab_gpl_future) - LOAD_OFFSET) { \
    		VMLINUX_SYMBOL(__start___ksymtab_gpl_future) = .;	\
    		*(SORT(___ksymtab_gpl_future+*))			\
    		VMLINUX_SYMBOL(__stop___ksymtab_gpl_future) = .;	\
    	}								\
    									\
    	/* Kernel symbol table: Normal symbols */			\
    	__kcrctab         : AT(ADDR(__kcrctab) - LOAD_OFFSET) {		\
    		VMLINUX_SYMBOL(__start___kcrctab) = .;			\
    		*(SORT(___kcrctab+*))					\
    		VMLINUX_SYMBOL(__stop___kcrctab) = .;			\
    	}								\
    									\
    	/* Kernel symbol table: GPL-only symbols */			\
    	__kcrctab_gpl     : AT(ADDR(__kcrctab_gpl) - LOAD_OFFSET) {	\
    		VMLINUX_SYMBOL(__start___kcrctab_gpl) = .;		\
    		*(SORT(___kcrctab_gpl+*))				\
    		VMLINUX_SYMBOL(__stop___kcrctab_gpl) = .;		\
    	}								\
    									\
    	/* Kernel symbol table: Normal unused symbols */		\
    	__kcrctab_unused  : AT(ADDR(__kcrctab_unused) - LOAD_OFFSET) {	\
    		VMLINUX_SYMBOL(__start___kcrctab_unused) = .;		\
    		*(SORT(___kcrctab_unused+*))				\
    		VMLINUX_SYMBOL(__stop___kcrctab_unused) = .;		\
    	}								\
    									\
    	/* Kernel symbol table: GPL-only unused symbols */		\
    	__kcrctab_unused_gpl : AT(ADDR(__kcrctab_unused_gpl) - LOAD_OFFSET) { \
    		VMLINUX_SYMBOL(__start___kcrctab_unused_gpl) = .;	\
    		*(SORT(___kcrctab_unused_gpl+*))			\
    		VMLINUX_SYMBOL(__stop___kcrctab_unused_gpl) = .;	\
    	}								\
    									\
    	/* Kernel symbol table: GPL-future-only symbols */		\
    	__kcrctab_gpl_future : AT(ADDR(__kcrctab_gpl_future) - LOAD_OFFSET) { \
    		VMLINUX_SYMBOL(__start___kcrctab_gpl_future) = .;	\
    		*(SORT(___kcrctab_gpl_future+*))			\
    		VMLINUX_SYMBOL(__stop___kcrctab_gpl_future) = .;	\
    	}								\
    									\
    	/* Kernel symbol table: strings */				\
            __ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) {	\
    		*(__ksymtab_strings)					\
    	}								\
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    内核镜像链接脚本调用关系为:

    #define RO_DATA(align) RO_DATA_SECTION(align)

    arch/arm/kernel/vmlinux.lds.S

    #ifdef CONFIG_DEBUG_RODATA
    	. = ALIGN(1<
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    最终在系统编译完成链接阶段,会将内核符号链接到对应section中。可以通过查看System.map文件看到对应内容,示例:

    ksymtab

    ……
    c05bcfb0 R __start___ksymtab
    c05bcfb0 R __start_builtin_fw
    c05bcfb0 R __start_pci_fixups_early
    c05bcfb0 R __start_pci_fixups_enable
    c05bcfb0 R __start_pci_fixups_final
    c05bcfb0 R __start_pci_fixups_header
    c05bcfb0 R __start_pci_fixups_resume
    c05bcfb0 R __start_pci_fixups_resume_early
    c05bcfb0 R __start_pci_fixups_suspend
    c05bcfb0 R __start_pci_fixups_suspend_late
    c05bcfb0 R __stop___bug_table
    c05bcfb8 R __ksymtab_PDE_DATA
    c05bcfc0 R __ksymtab____pskb_trim
    c05bcfc8 R __ksymtab____ratelimit
    c05bcfd0 R __ksymtab___aeabi_idiv
    c05bcfd8 R __ksymtab___aeabi_idivmod
    c05bcfe0 R __ksymtab___aeabi_lasr
    c05bcfe8 R __ksymtab___aeabi_llsl
    c05bcff0 R __ksymtab___aeabi_llsr
    c05bcff8 R __ksymtab___aeabi_lmul
    c05bd000 R __ksymtab___aeabi_uidiv
    c05bd008 R __ksymtab___aeabi_uidivmod
    c05bd010 R __ksymtab___aeabi_ulcmp
    ……
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    kstrtab

    ……
    c05c8ee0 r __kstrtab_reset_devices
    c05c8eee r __kstrtab_static_key_initialized
    c05c8f05 r __kstrtab_system_state
    c05c8f12 r __kstrtab_init_uts_ns
    c05c8f1e r __kstrtab_init_task
    c05c8f28 r __kstrtab_arm_elf_read_implies_exec
    c05c8f42 r __kstrtab_elf_set_personality
    c05c8f56 r __kstrtab_elf_check_arch
    c05c8f65 r __kstrtab_set_irq_flags
    c05c8f73 r __kstrtab_arm_check_condition
    c05c8f87 r __kstrtab_dump_fpu
    c05c8f90 r __kstrtab_thread_notify_head
    c05c8fa3 r __kstrtab_pm_power_off
    ……
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
  • 相关阅读:
    【Java I/O 流】文件的操作——java.io.File 类详解
    原生js实现简单的省市区联动效果
    debian 10 安装apache2 zabbix
    25.flink上下游算子之间数据是如何流动的(重要)
    利用Tensorrt实现int8量化
    使用js搭建简易的WebRTC实现视频直播
    spring-cloud-gateway启动失败以及springboo和springcloud版本对应关系总结
    Cortex-M架构MCU位带操作最详细解析(主要以STM32为例,包括判断哪些MCU可用)
    悬架模糊控制
    从编译后的代码,分析 Angular @Injectable 的工作原理
  • 原文地址:https://blog.csdn.net/weixin_42262944/article/details/126556617