• Linux 内核段符号简介


    一、内核段简介

    内核代码段的起始地址 _text :

     cat /proc/kallsyms | grep '\<_text\>'
    
    • 1

    内核代码段的结束地址 _etext:

    cat /proc/kallsyms | grep '\<_etext\>'
    
    • 1

    在这里插入图片描述

    内核代码段的大小:

    size = _etext - _text
    
    • 1

    /proc/kallsyms中的符号从 _text到 _etext 都是 T 和 t 类型:

    "T/t" :The symbol is in the text (code) section.
    
    • 1

    在这里插入图片描述
    在这里插入图片描述
    也会有少量的 W/w符号:

    "W/w" The symbol is a weak symbol that has not been specifically tagged as a weak object symbol.
    
    • 1

    _etext - _text之间的W/w符号都是weak text symbol。

    可以看到下面的符号位于_etext - _text之间,也是代码段符号,只不过是weak text symbol。
    在这里插入图片描述

    从/proc/kcore中也可以看到内核段,其起始地址与_text一样,但大小却大于上述的 size = _etext - _text,该内核段除了有内核代码段还有内核其他数据。
    在这里插入图片描述

    从_etext开始都是内核的其他的一些符号:
    比如只读数据符号:

    "R/r" The symbol is in a read only data section
    
    • 1

    在这里插入图片描述

    将上述 _text + memsize = 0xffffffff81000000 + 0x0000000001411000 = 0xffffffff82411000
    在这里插入图片描述
    内核段的最后是BSS data section。

    "B/b"	The symbol is in the BSS data section. This section typically contains zero-initialized or uninitialized data, although the exact behavior is system dependent.
    
    • 1

    0xffffffff82411000就是内核段的结尾,对于/proc/kallsyms中的符号,从0xffffffff82411000之后就是模块的符号,因此模块的符号地址一定大于内核段的符号。

    二、内核段符号顺序

    从内核段开始即内核代码段起始地址_text(T) 到 _end(B) 到内核段结尾,符号都是依次递增,从小到大排列。

    内核段中的大致存放顺序:

    "T/t" -> "R/r" -> "D/d" -> "B/b" 
    
    • 1

    即:内核代码段 ,内核只读数据段,内核数据段,内核BSS段。这些相同的符号类型都是在同一块区域,地址大小依次递增。

    同时对应的内核代码段 ,内核只读数据段,内核数据段,内核BSS段在物理内存上的布局也是这样:
    centos 7(3.10.0) 的内核只读数据段,内核数据段在物理内存上布局合在一起:
    /proc/iomem是整个系统物理地址空间的映射文件。
    在这里插入图片描述

    ubuntu 20.04 (5.15.0):
    在这里插入图片描述

    以下都是指内核段的虚拟地址空间布局:
    (1)内核代码段:

    static inline int init_kernel_text(unsigned long addr)
    {
    	if (addr >= (unsigned long)_sinittext &&
    	    addr <= (unsigned long)_einittext)
    		return 1;
    	return 0;
    }
    
    int core_kernel_text(unsigned long addr)
    {
    	if (addr >= (unsigned long)_stext &&
    	    addr <= (unsigned long)_etext)
    		return 1;
    
    	if (system_state == SYSTEM_BOOTING &&
    	    init_kernel_text(addr))
    		return 1;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    "T/t"	The symbol is in the text (code) section.
    
    • 1

    在这里插入图片描述
    备注:有些地址有多个符号,比如上述的 0xffffffff81000000,有_text 和 startup_64。

    当我们求内核代码段中函数的大小时,就可以用上下两条记录的地址值相减,就可以求出内核函数的大小,当然要考虑到有些地址有多个符号的情况。
    此方法只适用求内核代码段函数的大小,不适求模块函数的大小。

    (2)内核notes
    内核代码段和内核只读数据段直接还有一小块 notes 代码段,这一小部分也属于代码段,内核正常运行时并不会调用这部分代码段。

    ffffffff9d77c25f T _etext
    ffffffff9d77c260 T __start_notes
    ffffffff9d77c3dc T __stop_notes
    ffffffff9d77c3e0 R __start___ex_table
    ffffffff9d77e430 R __start___mc_table
    ffffffff9d77e430 R __stop___ex_table
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    (3)内核只读数据段:

    "R/r"	The symbol is in a read only data section
    
    • 1

    在这里插入图片描述
    当我们求内核数据段中数据的大小时,可以用上下两条记录的地址值相减,就可以求出内核数据的大小,比如系统调用表:

    ffffffffad4028f0 r __func__.50507
    ffffffffad402910 r __func__.50517
    ffffffffad402940 R sys_call_table
    ffffffffad4033a0 r __func__.37275
    ffffffffad4033b2 r str__vsyscall__trace_system_name
    ffffffffad4033c0 r setup_data_attr_group
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    (ffffffffad4033a0 - ffffffffad402940) = 0xa60 = 2656
    
    • 1

    即系统调用表的大小是2656字节

    2656 / 8 = 332 
    
    • 1

    系统调用表数组中的成员数量是332,即该版本系统调用的数目是332。

    (4)内核数据段:

    /**
     * core_kernel_data - tell if addr points to kernel data
     * @addr: address to test
     *
     * Returns true if @addr passed in is from the core kernel data
     * section.
     *
     * Note: On some archs it may return true for core RODATA, and false
     *  for others. But will always be true for core RW data.
     */
    int core_kernel_data(unsigned long addr)
    {
    	if (addr >= (unsigned long)_sdata &&
    	    addr < (unsigned long)_edata)
    		return 1;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    "D/d"	The symbol is in the initialized data section.
    
    • 1

    在这里插入图片描述
    (5)内核BSS段:

    "B/b"	The symbol is in the BSS data section. This section typically contains zero-initialized or uninitialized data, although the exact behavior is system dependent.
    
    • 1

    在这里插入图片描述

    (6)
    其中"D/d" -> “B/b” 之间有一部分内核启动时,内核退出时所用到的代码段,内核正常运行后,不会在调用这一部分内核代码段。
    这一部分也属于内核段,并且属于内核代码段,但是这部分是内核启动,内核退出时用到的代码段,内核正常运行时并不会调用,这部分内核代码段没有在 _text 到 _etext 之间。

    _sinittext到_einittext是内核初始化时运行的代码,内核正常运行时并不会调用。

    system_state == SYSTEM_BOOTING
    
    • 1
    /* Values used for system_state */
    extern enum system_states {
    	SYSTEM_BOOTING,
    	SYSTEM_RUNNING,
    	SYSTEM_HALT,
    	SYSTEM_POWER_OFF,
    	SYSTEM_RESTART,
    } system_state;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    static inline int init_kernel_text(unsigned long addr)
    {
    	if (addr >= (unsigned long)_sinittext &&
    	    addr <= (unsigned long)_einittext)
    		return 1;
    	return 0;
    }
    
    int core_kernel_text(unsigned long addr)
    {
    	if (addr >= (unsigned long)_stext &&
    	    addr <= (unsigned long)_etext)
    		return 1;
    
    	if (
    	 &&
    	    init_kernel_text(addr))
    		return 1;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    ffffffff9dd68000 D __init_begin
    ffffffff9dd68000 D __per_cpu_load
    ffffffff9dd85000 T early_idt_handler_array
    ffffffff9dd85000 T _sinittext
    ffffffff9dd85120 t early_idt_handler_common
    ffffffff9dd851d7 t is_nmi
    ffffffff9dd851f0 T startup_xen
    ffffffff9dd85205 t copy_bootdata
    ffffffff9dd853c1 t reset_early_page_tables
    ffffffff9dd85496 T __early_make_pgtable
    ffffffff9dd856e8 T early_make_pgtable
    ffffffff9dd8570b T x86_64_start_reservations
    ffffffff9dd85731 T x86_64_start_kernel
    ffffffff9dd858a8 T reserve_ebda_region
    ffffffff9dd85904 t set_reset_devices
    ffffffff9dd85919 t debug_kernel
    ffffffff9dd8592b t quiet_kernel
    ffffffff9dd8593d t init_setup
    ffffffff9dd85965 t rdinit_setup
    ffffffff9dd8598d t loglevel
    ffffffff9dd859e1 t do_early_param
    ffffffff9dd85a6f t initcall_blacklist
    ffffffff9dd85b1f t repair_env_string
    ffffffff9dd85b7b t unknown_bootoption
    ffffffff9dd85d04 T parse_early_options
    ffffffff9dd85d2c T parse_early_param
    ffffffff9dd85d69 W smp_setup_processor_id
    ffffffff9dd85d6f W thread_info_cache_init
    ffffffff9dd85d7b T start_kernel
    ffffffff9dd861e7 t kernel_init_freeable
    ffffffff9dd86406 T load_default_modules
    ffffffff9dd86411 t rootwait_setup
    .......
    ffffffff8a7eea46 t packet_init
    ffffffff8a7eea88 t wireless_nlevent_init
    ffffffff8a7eeac1 T netlbl_netlink_init
    ffffffff8a7eeade t netlbl_init
    ffffffff8a7eeb61 T netlbl_domhsh_init
    ffffffff8a7eec18 T netlbl_mgmt_genl_init
    ffffffff8a7eec2a T netlbl_unlabel_genl_init
    ffffffff8a7eec3c T netlbl_unlabel_init
    ffffffff8a7eed03 T netlbl_unlabel_defconf
    ffffffff8a7eed9d T netlbl_cipsov4_genl_init
    ffffffff8a7eedaf t dcbnl_init
    ffffffff8a7eedfc T net_sysctl_init
    ffffffff8a7eee4b t mpls_gso_init
    ffffffff8a7eee79 T _einittext
    
    ......
    
    ffffffff9df3be81 t nvmem_exit
    ffffffff9df3be93 t vmd_drv_exit
    ffffffff9df3bea5 t sock_diag_exit
    ffffffff9df3beb7 t exit_cgroup_netprio
    ffffffff9df3bf1d t exit_cgroup_cls
    ffffffff9df3bf3b t xt_fini
    ffffffff9df3bf59 t tcpudp_mt_exit
    ffffffff9df3bf70 t ipv4_netfilter_fini
    ffffffff9df3bf82 t cubictcp_unregister
    ffffffff9df3bf94 t xfrm_user_exit
    ffffffff9df3bfb2 t af_unix_exit
    ffffffff9df3bfda t inet6_exit
    ffffffff9df3c0b8 t packet_exit
    ffffffff9df3c0ec t dcbnl_exit
    ffffffff9df3c162 t mpls_gso_exit
    ffffffff9df3d000 R __smp_locks
    ffffffff9df3d000 R __init_end
    ffffffff9df44000 D in_suspend
    ffffffff9df44000 D __nosave_begin
    ffffffff9df44000 R __smp_locks_end
    ffffffff9df45000 B empty_zero_page
    ffffffff9df45000 B __bss_start
    ffffffff9df45000 D __nosave_end
    ffffffff9df46000 b dummy_mapping
    ffffffff9df47000 b level3_user_vsyscall
    ffffffff9df48000 B idt_table
    ffffffff9df49000 B debug_idt_table
    ffffffff9df4a000 B trace_idt_table
    ffffffff9df4b000 b bm_pte
    ffffffff9df4c000 B initcall_debug
    ffffffff9df4c004 B reset_devices
    ffffffff9df4c008 B saved_command_line
    ffffffff9df4c020 b msgbuf
    
    • 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
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83

    (7)
    通过读取vmlinux文件也可以发现,vmlinux 内核镜像 除了 _text到 _etext 这一块代码段之外,还有几块代码段:

    [root@localhost ~]# readelf -S vmlinux
    There are 36 section headers, starting at offset 0x13451a0:
    
    Section Headers:
      [Nr] Name              Type             Address           Offset
           Size              EntSize          Flags  Link  Info  Align
      [ 0]                   NULL             0000000000000000  00000000
           0000000000000000  0000000000000000           0     0     0
      [ 1] .text             PROGBITS         ffffffff81000000  00200000
           000000000077c25f  0000000000000000  AX       0     0     4096
      [ 2] .notes            NOTE             ffffffff8177c260  0097c260
           000000000000017c  0000000000000000  AX       0     0     4
      [ 3] __ex_table        PROGBITS         ffffffff8177c3e0  0097c3e0
           0000000000002050  0000000000000000   A       0     0     8
      [ 4] __mc_table        PROGBITS         ffffffff8177e430  0097e430
           0000000000000024  0000000000000000   A       0     0     4
      [ 5] .rodata           PROGBITS         ffffffff81800000  00a00000
           000000000030777e  0000000000000000   A       0     0     128
      [ 6] __bug_table       PROGBITS         ffffffff81b07780  00d07780
           0000000000006318  0000000000000000   A       0     0     1
      [ 7] __unwind_end_of_s PROGBITS         ffffffff81b0da98  00d0da98
           0000000000000068  0000000000000000   A       0     0     1
      [ 8] __unwind_unsafe_s PROGBITS         ffffffff81b0db00  00d0db00
           00000000000001d0  0000000000000000   A       0     0     1
      [ 9] .pci_fixup        PROGBITS         ffffffff81b0dcd0  00d0dcd0
           00000000000038b8  0000000000000000   A       0     0     8
      [10] .tracedata        PROGBITS         ffffffff81b11588  00d11588
           000000000000003c  0000000000000000   A       0     0     1
      [11] __ksymtab         PROGBITS         ffffffff81b115d0  00d115d0
           0000000000011f20  0000000000000000   A       0     0     16
      [12] __ksymtab_gpl     PROGBITS         ffffffff81b234f0  00d234f0
           000000000000df80  0000000000000000   A       0     0     16
      [13] __kcrctab         PROGBITS         ffffffff81b31470  00d31470
           0000000000008f90  0000000000000000   A       0     0     8
      [14] __kcrctab_gpl     PROGBITS         ffffffff81b3a400  00d3a400
           0000000000006fc0  0000000000000000   A       0     0     8
      [15] __ksymtab_strings PROGBITS         ffffffff81b413c0  00d413c0
           00000000000261c0  0000000000000000   A       0     0     1
      [16] __init_rodata     PROGBITS         ffffffff81b67580  00d67580
           00000000000000e8  0000000000000000   A       0     0     32
      [17] __param           PROGBITS         ffffffff81b67668  00d67668
           0000000000001d80  0000000000000000   A       0     0     8
      [18] __modver          PROGBITS         ffffffff81b693e8  00d693e8
           0000000000000c18  0000000000000000   A       0     0     8
      [19] .data             PROGBITS         ffffffff81c00000  00e00000
           0000000000166200  0000000000000000  WA       0     0     8192
      [20] .vvar             PROGBITS         ffffffff81d67000  00f67000
           00000000000000f0  0000000000000000  WA       0     0     16
      [21] .data..percpu     PROGBITS         0000000000000000  01000000
           000000000001d000  0000000000000000  WA       0     0     4096
      [22] .init.text        PROGBITS         ffffffff81d85000  01185000
           0000000000069e79  0000000000000000  AX       0     0     16
      [23] .init.data        PROGBITS         ffffffff81df0000  011f0000
           0000000000124020  0000000000000000  WA       0     0     8192
      [24] .x86_cpu_dev.init PROGBITS         ffffffff81f14020  01314020
           0000000000000018  0000000000000000   A       0     0     8
      [25] .parainstructions PROGBITS         ffffffff81f14038  01314038
           000000000001d92c  0000000000000000   A       0     0     8
      [26] .altinstructions  PROGBITS         ffffffff81f31968  01331968
           00000000000069d8  0000000000000000   A       0     0     1
      [27] .altinstr_replace PROGBITS         ffffffff81f38340  01338340
           000000000000217e  0000000000000000  AX       0     0     1
      [28] .iommu_table      PROGBITS         ffffffff81f3a4c0  0133a4c0
           00000000000000f0  0000000000000000   A       0     0     8
      [29] .apicdrivers      PROGBITS         ffffffff81f3a5b0  0133a5b0
           0000000000000028  0000000000000000  WA       0     0     8
      [30] .exit.text        PROGBITS         ffffffff81f3a5d8  0133a5d8
           0000000000001ba8  0000000000000000  AX       0     0     1
      [31] .smp_locks        PROGBITS         ffffffff81f3d000  0133d000
           0000000000007000  0000000000000000   A       0     0     4
      [32] .data_nosave      PROGBITS         ffffffff81f44000  01344000
           0000000000001000  0000000000000000  WA       0     0     4
      [33] .bss              NOBITS           ffffffff81f45000  01345000
           000000000030c000  0000000000000000  WA       0     0     4096
      [34] .brk              NOBITS           ffffffff82251000  01345000
           0000000000429000  0000000000000000  WA       0     0     1
      [35] .shstrtab         STRTAB           0000000000000000  01345000
           000000000000019b  0000000000000000           0     0     1
    Key to Flags:
      W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
      L (link order), O (extra OS processing required), G (group), T (TLS),
      C (compressed), x (unknown), o (OS specific), E (exclude),
      l (large), p (processor specific)
    
    • 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
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83

    其中的下面几个 section 都不属于内核 text section,但都是vmlinux 镜像中的代码段:

      [ 2] .notes            NOTE             ffffffff8177c260  0097c260
           000000000000017c  0000000000000000  AX       0     0     4
    
    • 1
    • 2
      [22] .init.text        PROGBITS         ffffffff81d85000  01185000
           0000000000069e79  0000000000000000  AX       0     0     16
    
    • 1
    • 2
      [27] .altinstr_replace PROGBITS         ffffffff81f38340  01338340
           000000000000217e  0000000000000000  AX       0     0     1
    
    • 1
    • 2
      [30] .exit.text        PROGBITS         ffffffff81f3a5d8  0133a5d8
           0000000000001ba8  0000000000000000  AX       0     0     1
    
    • 1
    • 2

    X (execute),表示可执行,也就是代码段。

    总结

    内核段的符号从代码段的符号_text 到 BSS段的符号_end,之后就是模块的相关符号了。模块的符号肯定大于内核段中的符号。

    备注:模块的符号大于内核段中的符号这是相对于x86_64,对于ARM64,在Linux 4.6 中 将内核镜像移到vmalloc的区域了。因此在ARM64,Linux 4.6 以上的版本内核段中的符号都大于模块的符号。
    比如:

    5.4.18-74-generic aarch64 GNU/Linux
    
    • 1
    ffffffc010080000 T _text
    ......
    ffffffc0117f4000 B _end
    ffffffc0092dc380 d $d   [virt_wifi]
    ffffffc0092da024 r $d   [virt_wifi]
    ffffffc0092da024 r _note_6      [virt_wifi]
    ......
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    内核段的地址都大于模块的地址。

    参考资料

    https://blog.csdn.net/weixin_45030965/article/details/125164642
    https://blog.csdn.net/weixin_45030965/article/details/125055828
    https://tinylab.org/lwn-531148/

  • 相关阅读:
    Linux 内核参数:watermark_boost_factor
    Docker学习资料集(从入门到实践)
    痞子衡嵌入式:IAR环境下无法直接下载调试i.MXRT分散链接工程的解决方案(宏文件.mac+双Flashloader)
    day57 动规.p17 子序列
    彩虹女神跃长空,Go语言进阶之Go语言高性能Web框架Iris项目实战-模板与数据库EP02
    nginx 配置防盗链(了解)
    HDFS_DFS(三):window10上配置Hadoop
    JAVA小知识20:万字详解List与ArrayList
    【CV】第 9 章:使用多任务深度学习的动作识别
    PlantUML绘制类图
  • 原文地址:https://blog.csdn.net/weixin_45030965/article/details/126854838