解析 Linux 内核可装载模块的版本检查机制

转自:http://www.ibm.com/developerworks/cn/linux/l-cn-kernelmodules/

为保持 Linux 内核的稳定与可持续发展,内核在发展过程中引进了可装载模块这一特性。内核可装载模块就是可在内核运行时加载到内核的一组代码。通常 , 我们会在两个版本不同的内核上装载同一模块失败,即使是在两个相邻的补丁级(Patch Level)版本上。这是因为内核在引入可装载模块的同时,对模块采取了版本信息校验。这是一个与模块代码无关,却与内核相连的机制。该校验机制保证了内核装载的模块是用户认可的,且安全的。本文将从内核模块发布者的角度思考模块版本检查机制,并从开发者与授权 root 用户的角度去使用及理解该机制。

内核可装载模块概述

Linux 在发展过程中(即自 Linux 1.2 之后)引进了模块这一重要特性,该特性提供内核可在运行时进行扩展。可装载模块(Loadable Kernel Module,即 LKM)也被称为模块,就是可在内核运行时加载到内核的一组目标代码(并非一个完整的可执行程序)。这就意味着在重构和使用可装载模块时并不需要重新编译内核。模块依据代码编写与编译时的位置可分:内部模块和外部模块,即 in-tree module 和 out-of-tree module,在内核树外部编写并构建的模块就是外部模块。如果只是认为可装载模块就是外部模块或者认为在模块与内核通讯时模块是位于内核的外部的,那么这在 Linux 下均是错误的。当模块被装载到内核后,可装载模块已是内核的一部分。另外,我们使用的 Linux 发行版在系统启动过程 initrd 中已使用了必要的模块,除非我们只讨论基础内核(base kernel)。本文主要是对 Linux 2.6 的外部模块进行讨论的。

可装载模块在 Linux 2.6 与 2.4 之间存在巨大差异,其最大区别就是模块装载过程变化(如 图 1所示,在 Linux 2.6 中可装载模块在内核中完成连接)。其他一些变化大致如下:

  • 模块的后缀及装载工具;

对于使用模块的授权用户而言,模块最直观的改变应是模块后缀由原先的 .o 文件(即 object)变成了 .ko 文件(即 kernel object)。同时,在 Linux 2.6 中,模块使用了新的装卸载工具集 module-init-tools(工具 insmod 和 rmmod 被重新设计)。模块的构建过程改变巨大,在 Linux 2.6 中代码先被编译成 .o 文件,再从 .o 文件生成 .ko 文件,构建过程会生成如 .mod.c、.mod.o 等文件。

  • 模块信息的附加过程;

在 Linux 2.6 中,模块的信息在构建时完成了附加;这与 Linux 2.4 不同,先前模块信息的附加是在模块装载到内核时进行的(在 Linux 2.4 时,这一过程由工具 insmod 完成)。

  • 模块的标记选项。

在 Linux 2.6 中,针对管理模块的选项做了一些调整,如取消了 can_unload 标记(用于标记模块的使用状态),添加了 CONFIG_MODULE_UNLOAD 标记(用于标记禁止模块卸载)等。还修改了一些接口函数,如模块的引用计数。

图 1. 模块在内核中完成连接

发展到 Linux 2.6,内核中越来越多的功能被模块化。这是由于可装载模块相对内核有着易维护,易调试的特点。可装载模块还为内核节省了内存空间,因为模块一般是在真正需要时才被加载。根据模块作用,可装载模块还可分三大类型:设备驱动、文件系统和系统调用。另须指出的是,虽然可装载模块是从用户空间加载到内核空间的,但是并非用户空间的程序。

回页首

模块的版本检查

Linux 的迅速发展致使相邻版本的内核之间亦存在较大的差异,即在版本补丁号(Patch Level,即内核版本号的第四位数)相邻的内核之间。为此 Linux 的开发者为了保证内核的稳定,Linux 在加载模块到内核时对模块采用了版本校验机制。当被期望加载模块的系统环境与模块的构建环境相左时,通常会出现如清单 1 所示的装载模块失败。

清单 1. 装载模块失败
 # insmod ./hello/hello.ko
 insmod: error inserting ‘./hello/hello.ko‘: -1 Invalid module format 

 # dmesg | grep hello
 [ 9206.599843] hello: disagrees about version of symbol module_layout

清单 1 中,模块 hello.ko 构建时的环境与当前系统不一致,导致工具 insmod 在尝试装载模块 hello.ko 到内核时失败。hello.ko 是一个仅使用了函数 printk 的普通模块(您可在示例源码中找到文件 hello/hello.c)。我们通过命令 dmesg(或者您也可以查看系统日志文件如 /var/log/messages 等,如果您启用了这些系统日志的话)获取模块装载失败的具体原因,模块 hello.ko 装载失败是由于模块中 module_layout 的导出符号的版本信息与当前内核中的不符。函数 module_layout 被定义在内核模块版本选项 MODVERSIONS(即内核可装载模块的版本校验选项)之后。清单 2所示为 module_layout 在内核文件 kernel/module.c 中的定义。

清单 2. 函数 module_layout
 /* kernel/module.c */
 #ifdef CONFIG_MODVERSIONS
 void module_layout(struct module *mod,
       struct modversion_info *ver,
       struct kernel_param *kp,
       struct kernel_symbol *ks,
       struct tracepoint * const *tp)
 {
 }
 EXPORT_SYMBOL(module_layout);
 #endif
清单 3. 结构体 modversion_info
 /* include/linux/module.h */
 struct modversion_info
 {
     unsigned long crc;
     char name[MODULE_NAME_LEN];
 };

正如您所想,函数 module_layout 的第二个参数 ver 存储了模块的版本校验信息。结构体 modversion_info 中保存了用于模块校验的 CRC(Cyclic Redundancy Check,即循环冗余码校验)值(见 清单 3)。Linux 对可装载模块采取了两层验证:模块的 CRC 值校验和 vermagic 的检查。其中模块 CRC 值校验针对模块(内核)导出符号,是一种简单的 ABI(即 Application Binary Interface)一致性检查,清单 1中模块 hello.ko 加载失败的根本原因就是没有通过 CRC 值校验(即 module_layout 的 CRC 值与当前内核中的不符);而模块 vermagic(即 Version Magic String)则保存了模块编译时的内核版本以及 SMP 等配置信息(见 清单 4,模块 hello.ko 的 vermagic 信息),当模块 vermagic 与主机信息不相符时亦将终止模块的加载。

清单 4. 模块的 vermagic 信息
 # uname – r
 2.6.38-10-generic 

 # modinfo ./hello/hello.ko
 filename:       ./hello/hello.ko
 license:        Dual BSD/GPL
 srcversion:     31FE72DA6A560C890FF9B3F
 depends:
 vermagic:       2.6.38-9-generic SMP mod_unload modversions

通常,内核与模块的导出符号的 CRC 值被保存在文件 Module.symvers 中,该文件需在开启内核配置选项 CONFIG_MODVERSIONS 之后并完全编译内核获得(或者您也可在编译外部模块后获得该文件,保存的是模块的导出符号的 CRC 信息),其信息的保存格式如清单 5 所示。

清单 5. 导出符号的 CRC 值
 0x1de386dd  module_layout  vmlinux  EXPORT_SYMBOL
 <CRC>        <Symbol>  <module>

Linux 内核在进行模块装载时先完成模块的 CRC 值校验,再核对 vermagic 中的字符信息,图 2展示了内核中与模块版本校验相关的函数的调用过程(分别在函数 setup_load_info 和 check_modinfo 中完成校验)。Linux 使用 GCC 中的声明函数属性 __attribute__ 完成对模块的版本信息附加。构建的模块存在几个 section,如 .modinfo、.gnu.linkonce.this_module 和 __versions 等,这些 ELF 小节(即 section)保存了模块校验所需的信息(关于这些 section 信息的附加过程,您可查看模块构建时生成的文件 <module>.mod.c 及工具 modpost,见 清单 8和 清单 15)。

图 2. 模块的两层版本校验过程

为了更好的理解可装载模块,我们查看内核头文件 include/linux/module.h,它不仅定义了上述中的 struct modversion_info(见 清单 3)还定义了 struct module 等结构体。模块 CRC 值校验查看的是就是模块 __versions 小节的内容,即是附加的 struct modversion_info 信息。模块的 CRC 校验过程在函数 setup_load_info 中完成。Linux 使用 .gnu.linkonce.this_module 小节来解决模块对 struct module 信息的附加。文件 kernel/module.c 中的函数 check_modinfo 完成了主机与模块的 vermagic 值的对比(见 清单 6)。清单 6 中函数 get_modinfo 用于获取内核中的 vermagic 信息,模块 vermagic 信息则被保存在了 ELF 的 .modinfo 小节中。

清单 6. 函数 check_modinfo
 /* kernel/module.c */
 static int check_modinfo(struct module *mod, struct load_info *info)
 {
  const char *modmagic = get_modinfo(info, "vermagic"); 

  ...
  } else if (!same_magic(modmagic, vermagic, info->index.vers)) {
    ...
  }
  ... 

  return 0;
 }

内核空间与用户空间

操作系统须负责程序的独立操作并保护资源不受非法访问,而这一功能在现代 CPU 中以设计不同的操作模式(级别)来实现。内核运行在 CPU 的最高级别,即内核态,也被称为超级用户态;而应用程序则运行在最低级别,即用户态。由此系统内存在 Linux 中可分为两个不同的区域:内核空间与用户空间。模块运行在内核空间里,而应用程序则运行在对应的用户空间中。

须指出的是模块的 vermagic 信息来自内核头文件 include/linux/vermagic.h 中的宏 VERMAGIC_STRING,其中宏 UTS_RELEASE 保存了内核版本信息(见 清单 7)。与其关联的头文件 include/generated/utsrelease.h 需经内核预编译生成,即通过命令 make 或 make modules_prepare 等。

清单 7. 宏 VERMAGIC_STRING
 /* kernel/module.c */
 static const char vermagic[] = VERMAGIC_STRING; 

 /* include/linux/vermagic.h */
 #define VERMAGIC_STRING                         \
     UTS_RELEASE " "                         \
     MODULE_VERMAGIC_SMP MODULE_VERMAGIC_PREEMPT             \
     MODULE_VERMAGIC_MODULE_UNLOAD MODULE_VERMAGIC_MODVERSIONS   \
     MODULE_ARCH_VERMAGICLINE

回页首

模块的装载与卸载

上述中,我们在装载模块时使用了工具 insmod。在 Linux 2.6 中,工具 insmod 被重新设计并作为工具集 module-init-tools 中的一个程序,其通过系统调用 sys_init_module(您可查看头文件 include/asm-generic/unistd.h)衔接了模块的版本检查,模块的装载等功能(如 图 3所示)。module-init-tools 是为 2.6 内核设计的运行在 Linux 用户空间的模块装卸载工具集,其包含的程序 rmmod 用于卸载当前内核中的模块。

图 3. 模块的装卸载

表 1. 工具集 module-init-tools 中的部分程序
名称 说明
insmod 装载模块到当前运行的内核中
rmmod 从当前运行的内核中卸载模块
lsmod 显示当前内核已加装的模块信息
modinfo 检查与内核模块相关联的目标文件,并打印出所有得到的信息
modprobe 利用 depmod 创建的依赖关系文件自动加载相关的模块
depmod 创建一个内核可装载模块的依赖关系文件,modprobe 用它来自动加载模块

值得一提的是在 module-init-tools 中可用于模块装卸载的程序 modprobe。程序 modprobe 的内部函数调用过程正如您所想与 insmod 类似,只是其装载过程会查找一些模块装载的配置文件,且 modprobe 在装载模块时可解决模块间的依赖性,即若有必要,程序 modprobe 会在装载一个模块时自动加载该模块依赖的其他模块。

回页首

其他一些细节

从用户空间装载模块到内核时,Linux 还对用户权限进行了检查。模块的装载须是获得 CAP_SYS_MODULE 权限的超级用户,这正是模块装载时最先检查的内容(见 图 2)。在 Linux 2.6 中,模块在构建时生成了一些临时文件,如 .o 文件、.mod.o 文件等。了解这些文件的生成有助于我们更好的理解 Linux 2.6 的内核模块构建过程以及版本信息的检查等内容。文件 .o 是模块代码(即 .c 文件)经编译后获得的目标文件,文件 .mod.o 则对应文件 .mod.c。文件 <module>.mod.c 是对 <modulue>.c 的扩展,清单 8展示了文件 kobject-example.mod.c 的内容 ( 即模块 kobject-example.ko 的 .mod.c 文件 ),您可见到与模块版本检查相关三个小节。

清单 8. 文件 kobject-example.mod.c
 # cat ./kobject/kobject-example.mod.c
 ... 

 MODULE_INFO(vermagic, VERMAGIC_STRING); 

 struct module __this_module
 __attribute__((section(".gnu.linkonce.this_module"))) = {
 ...
 }; 

 static const struct modversion_info ____versions[]
 __used
 __attribute__((section("__versions"))) = {
 ...
 }; 

 static const char __module_depends[]
 __used
 __attribute__((section(".modinfo"))) =
"depends="; 

 MODULE_INFO(srcversion, "B06F9B8B7AB52AEED247B9F");

清单 8 中显示了模块 kobject-example.ko 中的三个 section 以及宏 MODULE_INFO,最后一行 srcversion 则需开启内核配置选项 MODULE_SRCVERSION_ALL。经上述,我们知道这三个 section 正是模块版本检查的附加信息。我们通过工具 objdump 查看 .modinfo 小节(见 清单 9, 即模块的 vermagic 信息)。<module>.ko 的附加信息合并自文件 <module>.o 与文件 <module>.mod.o。内核工具 modpost 完成了一这步骤,且该工具是 Linux 2.6 内核模块构建时所必须的。

清单 9. 使用工具 objdump 查看 .modinfo 小节
 # objdump --section=.modinfo -s hello/hello.o 

 hello/hello.o:     file format elf64-x86-64 

 Contents of section .modinfo:
 0000 6c696365 6e73653d 4475616c 20425344  license=Dual BSD
 0010 2f47504c 00                          /GPL. 

 # modinfo hello/hello.o
 filename:       hello/hello.o
 license:        Dual BSD/GPL 

 # objdump --section=.modinfo -s hello/hello.mod.o
 ...
 # objdump --section=.modinfo -s hello/hello.ko
 ...

经上述,我们可知内核树的顶层 Makefile 文件包含了内核版本的信息,且该信息经编译后被添加到模块的(头文件 include/generated/utsrelease.h 保存的内核版本信息来自顶层 Makefile)。表 1中,工具 lsmod 打开文件 /proc/modules 查询当前内核中已装载的模块(见清单 10),文件 /proc/modules 还被 rmmod 在卸载模块时使用。另外,若您在装载模块 hello.ko 后没能在终端下看到相应的字符串输出,则需检查文件 /proc/sys/kernel/printk,并重设下消息级别。

清单 10. 工具 lsmod 的使用
 # insmod ./kobject/kobject-example.ko 

 # ls /sys/kernel/kobject_example/
 bar  baz  foo 

 # lsmod | grep kobject
 kobject_example        12857  0 

 # cat /proc/modules | grep kobject
 kobject_example 12857 0 - Live 0xffffffffa0523000

Linux 2.6 构建模块时工具 modpost 被 scripts/Makefile.modpost 调用,生成 <module>.mod.c 及文件 Module.symvers(见 清单 15)。在开启内核选项 CONFIG_MODVERSIONS 之后,文件 Makefile.Build 会调用工具 genksyms(现位于内核树 scripts/genksyms 目录下,在 Linux 2.4 时是模块工具集 Modutils 的一部分)生成 CRC 信息(见 清单 11)。其中代码 call cmd_gensymtypes 就是对工具 genksyms 的调用。另外一个较为明晰的方式是,使用工具 objdump 或 readelf 查看相关的 ELF 小节,并使用 make – n 查看模块构建过程。

清单 11. 文件 Makefile.Build 的部分内容
 ifndef CONFIG_MODVERSIONS
 cmd_cc_o_c = $(CC) $(c_flags) -c -o [email protected] $<
 else
 cmd_cc_o_c = $(CC) $(c_flags) -c -o $(@D)/.tmp_$(@F) $<
 cmd_modversions =              \
  if $(OBJDUMP) -h $(@D)/.tmp_$(@F) | grep -q __ksymtab; then  \
    $(call cmd_gensymtypes, $(KBUILD_SYMTYPES))    \
        > $(@D)/.tmp_$(@F:.o=.ver);      \
                  \
    $(LD) $(LDFLAGS) -r -o [email protected] $(@D)/.tmp_$(@F)     \
      -T $(@D)/.tmp_$(@F:.o=.ver);      \
    rm -f $(@D)/.tmp_$(@F) $(@D)/.tmp_$(@F:.o=.ver);  \
  else                \
    mv -f $(@D)/.tmp_$(@F) [email protected];        \
  fi;
 endif

回页首

模块的构建与测试

为内核构建外部模块前,我们须准备一颗内核源码树(kernel source tree)。内核源码树就是一套包含系统配置及内核头文件的内核目录树。须指出的是 Linux 2.6 的内核源码树与 2.4 的不同,先前的内核只需一套内核头文件就可以了,但在 2.6 的内核源码树中还需存在一些目标文件及工具,如 scripts/mod/modpost 等。清单 12 所示是从内核源码进行内核模块预编译以此生成内核树,当然您也可使用 Linux 发行版的内核源码树(系统内核树一般存放在 /lib/modules/<kernel version>/build,如果存在的话)。

清单 12. 预编译内核模块
 # make menuconfig
 # make modules_prepare
 #

当然,我们最先须根据主机的硬件信息产生内核配置文件 .config。您可使用命令 make menuconfig 或 make config 等来配置与模块相关的选项(清单 13与 清单 14相互对应,显示了模块相关的内核配置选项)。设置选项 CONFIG_MODULES=y 以及 CONFIG_MODVERSIONS=y 使内核支持模块的版本检查。另须注意的是,模块预编译并不生成 Module.symvers 文件,即使您开启了 CONFIG_MODVERSIONS 选项。因此最好的方式是完全编译 Linux 内核。

清单 13. 使用 make menuconfig 配置内核模块选项
 # make menuconfig
 --- Enable loadable module support
    [ ]   Forced module loading
    [*]   Module unloading
    [ ]     Forced module unloading
    [*]   Module versioning support
    [*]   Source checksum for all modules
清单 14. 模块相关的内核配置选项
 CONFIG_MODULES
 CONFIG_MODULE_FORCE_LOAD
 CONFIG_MODULE_UNLOAD
 CONFIG_MODULE_FORCE_UNLOAD
 CONFIG_MODVERSIONS
 CONFIG_MODULE_SRCVERSION_ALL

内核 2.6 时,我们常为模块的构建编写一个 Makefile 文件,但仍可使用类似内核 2.4 下的模块构建命令。清单 15 展示了外部模块构建的 make 命令,其中 $KDIR 是内核树的绝对路径,$MDIR 是期望构建的模块的绝对路径(若是内部模块则可使用 make CONFIG_EXT2_FS=m …)。

清单 15. 构建外部模块
 # make -C $KDIR M=$MDIR [target] 

 # make -C /lib/modules/2.6.38-10-generic/build M=$PWD/hello  modules
 make: Entering directory `/usr/src/linux-headers-2.6.38-10-generic‘
 CC [M] /home/harris/work/samples/hello/hello.o
 Building modules, stage 2.
 MODPOST 1 modules
 CC /home/harris/work/samples/hello/hello.mod.o
 LD [M] /home/harris/work/samples/hello/hello.ko
 make: Leaving directory `/usr/src/linux-headers-2.6.38-10-generic‘

回页首

结束语

虽然本文已尽量集中描述可装载模块的版本检查机制,但是仍然涉及了非常多的内容。您需花一些时间来了解这些看似与模块不相关的内容,如 /proc、/sys 文件系统、ELF(即 Executable and Linkable Format)格式等,以此更好的全面理解内核可装载模块以及模块版本版本检查机制。另外,由于文章讨论的是 Linux 2.6 的外部模块,没有清晰的讲述内核的 Kbuild 系统及 Makefile 文件,但这对于模块亦是重要的内容。

时间: 2024-10-21 04:59:33

解析 Linux 内核可装载模块的版本检查机制的相关文章

Linux内核如何装载和启动一个可执行程序(转)

原文:http://www.cnblogs.com/petede/p/5351696.html 实验七:Linux内核如何装载和启动一个可执行程序 姓名:李冬辉 学号:20133201 注: 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 云课堂笔记: (1)可执行文件的创建 C代码(.c) - 经过编译器预处理,编译成汇编代码(.asm) - 汇编器,生成目标代码(.o) - 链接

Linux内核如何装载和启动一个可执行程序-----实验7

2015108 李泽源 Linux内核如何装载和启动一个可执行程序 理解编译链接的过程和ELF可执行文件格式,详细内容参考本周第一节: 编程使用exec*库函数加载一个可执行文件,动态链接分为可执行程序装载时动态链接和运行时动态链接,编程练习动态链接库的这两种使用方式,详细内容参考本周第二节: 使用gdb跟踪分析一个execve系统调用内核处理函数sys_execve ,验证您对Linux系统加载可执行程序所需处理过程的理解,详细内容参考本周第三节:推荐在实验楼Linux虚拟机环境下完成实验.

深入浅出实例解析linux内核container_of宏

做一件事情首先应该知道它的目的是什么. container_of的目的:如何通过结构中的某个变量获取结构本身的指针. 总体思路:假想一下,你的结构体中有好几个成员,你如何通过里面的"任一成员"获取整个结构体的首地址呢.container_of的做法就是通过typeof定义一个与"任一成员"同类型的指针变量pvar_a(假设变量名就是pvar_a),并让指针变量pvar_a指向这个"任一成员",然后用 "pvar_a的地址" 减

linux内核hello world模块编写

#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> int param = 0; /* 设备模块注册时执行的初始化函数 */ static int __init initialization_module(void) { printk("Hello world.\n"); printk("param = %d.\n", param)

实验七:Linux内核如何装载和启动一个可执行程序

原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 题目自拟,内容围绕对Linux内核如何装载和启动一个可执行程序 可以结合实验截图.ELF可执行文件格式.用户态的相关代码等 博客内容中需要仔细分析新可执行程序的执行起点及对应的堆栈状态等 总结部分需要阐明自己对“Linux内核装载和启动一个可执行程序”的理解 实验报告: 理解编译链接的过程和ELF可执行文件格式: C代码——预处理——汇

Linux内核通知链模块

通知链描述 大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣.为了满足这个需求,也即是让某个子系统在发生某个事件时通知其它的子系统,Linux内核提供了通知链的机制.通知链表只能够在内核的子系统之间使用,而不能够在内核与用户空间之间进行事件的通知. 通知链表是一个函数链表,链表上的每一个节点都注册了一个函数.当某个事情发生时,链表上所有节点对应的函数就会被执行.所以对于通知链表来说有一个通知方与一个接收方.在通知这个事件时所运行的函数由被通知方决定,实际上也即是被通

Linux内核的主要模块

进程调度SCHED 进程调度指的是系统对进程的多种状态之间转换的策略.分别是:SCHED_OTHER.SCHED_FIFO.SCHED_RR. 内存管理MMU 内存管理是多个进程间的内存共享策略.在Linxu系统中,内存管理的主要概念是虚拟内存. 没个进程的虚拟内存有不同的地址空间,多个进程虚拟内存不会冲突.虚拟内存的大小通常是物理内存的2倍. 虚拟文件系统 Linxu系统下支持多中文件系统,最常用的是ext2和ext3,2中系统之间可以相互转换. 网络接口 网络接口分为网络协议和网络驱动程序,

解析Linux内核的基本的模块管理与时间管理操作---超时处理【转】

转自:http://www.jb51.net/article/79960.htm 这篇文章主要介绍了Linux内核的基本的模块管理与时间管理操作,包括模块加载卸载函数的使用和定时器的用法等知识,需要的朋友可以参考下 内核模块管理Linux设备驱动会以内核模块的形式出现,因此学会编写Linux内核模块编程是学习linux设备驱动的先决条件. Linux内核的整体结构非常庞大,其包含的组件非常多.我们把需要的功能都编译到linux内核,以模块方式扩展内核功能. 先来看下最简单的内核模块 ? 1 2

Linux内核分析:实验七--Linux内核如何装载和启动一个可执行程序

刘畅 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 写在前面 本次实验着力分析Linux内核装载和启动一个可执行程序的过程,其中包括可执行文件格式的分析.可执行文件的装载和链接的过程,并通过GDB跟踪execve系统调用来梳理Linux系统加载可执行程序的过程. 可执行文件的格式分析 相对于其它文件类型,可执行文件可能是一个操作系统中最重要的文件类型,因为它们是完成操作的真正执行者.可