10. 技术细节

10.1. 工作原理

insmod 执行一个init_module系统调用,将 LKM 加载到内核内存中。加载是容易的部分。但是,内核如何知道使用它呢?答案是init_module系统调用在加载 LKM 后立即调用 LKM 的初始化例程。insmodinit_moduleLKM 中名为init_module的子例程的地址传递给

作为其初始化例程。init_module(这令人困惑 -- 每个 LKM 都有一个名为init_module).

的子例程,而基础内核有一个同名的系统调用,可以通过标准 C 库中也名为init_module的子例程访问。LKM 作者设置了init_module来调用一个内核函数,该函数注册 LKM 包含的子例程。例如,字符设备驱动程序的子例程可能会调用内核的register_chrdev子例程可能会调用内核的子例程,传递它打算驱动的设备的主设备号和次设备号,以及它的 "open" 例程的地址作为参数。

在基础内核表中记录,当内核想要打开该特定设备时,它应该调用我们 LKM 中的 open 例程。init_module但是,聪明的读者现在会问,LKM 的子例程可能会调用内核的子例程如何知道基础内核的

子例程的地址。这不是系统调用,而是绑定到基础内核的普通子例程。调用它意味着分支到它的地址。那么,我们的 LKM 没有在任何接近基础内核的地方编译,如何知道该地址呢?答案是在 insmod 时发生的重定位。

在 Linux 2.4 和 Linux 2.6 之间,重定位的发生方式根本不同。在 2.6 中,insmod 几乎只是将 LKM 文件(.ko 文件)的原始内容传递给内核,内核进行重定位。在 2.4 中,insmod 进行重定位,并传递一个完全链接的模块镜像,准备填充到内核内存中给内核。以下描述涵盖了 2.4 的情况。子例程可能会调用内核的insmod 充当重定位链接器/加载器。LKM 目标文件包含对符号的外部引用。insmod 执行query_module子例程可能会调用内核的系统调用,以查找现有内核导出的各种符号的地址。的外部引用。insmod 执行是其中之一。子例程可能会调用内核的返回子例程可能会调用内核的.

代表的地址,并且 insmod 将其修补到 LKM 中,LKM 在其中引用的外部引用。insmod 执行如果您想查看 insmod 可以从系统调用中获取的信息类型,请查看.

/proc/ksyms的内容。请注意,一些 LKM 调用其他 LKM 中的子例程。它们可以这样做,因为 LKM 目标文件中有__ksymtab.kstrtab的内容。请注意,一些 LKM 调用其他 LKM 中的子例程。它们可以这样做,因为 LKM 目标文件中有__ksymtab节。这些节共同列出了 LKM 目标文件中的外部符号,这些符号应该可以被将来插入的其他 LKM 访问。insmod 查看

,并告诉内核将这些符号添加到其导出的内核符号表中。要亲自查看这一点,请插入 LKMmsdos.o系统调用中获取的信息类型,请查看,然后在/proc/ksyms中注意符号fat_add_cluster(它是/proc/ksymsfat.o要亲自查看这一点,请插入 LKMLKM 中的子例程的名称)。任何随后插入的 LKM 都可以分支到

,事实上

正是这样做的。10.2. .modinfo 节ELF 目标文件由各种命名节组成。其中一些是目标文件的基本部分,例如.text节包含加载器加载的可执行代码。但是您可以创建任何您想要的节,并让特殊程序使用它。对于 Linux LKM 的目的,有.text.modinfo

节。LKM 不必具有名为.text的节才能工作,但是您应该用于编写 LKM 代码的宏会导致生成一个节,所以它们通常会这样做。

要查看目标文件的节,包括

objdump msdos.o --section-headers
.modinfo.text节(如果存在),请使用 objdump 程序。例如
objdump msdos.o --full-contents --section=.modinfo

要查看 msdos LKM 的目标文件中的所有节.text要查看

.modinfo.text节的内容.text您可以使用 modinfo 程序来解释

.modinfo

节中有什么,谁使用它?insmod的内容。请注意,一些 LKM 调用其他 LKM 中的子例程。它们可以这样做,因为 LKM 目标文件中有__ksymtab.modinfo节用于以下目的:它包含构建模块的内核发布版本号。即,编译模块时使用的内核源代码树的头文件。节用于以下目的:insmod 使用该信息,如第 6 节中所述。

它描述了 LKM 参数的形式。insmod 使用此信息将您在 insmod 命令行上提供的参数格式化为数据结构初始值,insmod 在加载 LKM 时将其插入到 LKM 中。

10.3. __ksymtab 和 .kstrtab 节

您经常在 LKM 目标文件中找到的另外两个节分别名为

__ksymtab系统调用中获取的信息类型,请查看.kstrtab。它们一起列出了 LKM 中应可供内核其他部分访问(导出)的符号。符号只是 LKM 中地址的文本名称。LKM A 的目标文件可以通过名称引用 LKM B 中的地址(例如,

getBinfo

”)。当您插入 LKM A 时,在插入 LKM B 之后,insmod 可以将 LKM B 中名为

的数据/子例程的实际地址插入到 LKM A 中。

有关符号绑定的更多令人费解的细节,请参阅第 10.1 节系统调用中获取的信息类型,请查看):

10.4. Ksymoops 符号insmod 在加载时向 LKM 添加了一堆导出的符号。这些符号都旨在帮助 ksymoops 完成其工作。ksymoops 是一个解释和显示 "oops" 的程序。"oops" 显示是 Linux 内核在检测到内部内核错误(并因此终止进程)时显示的内容。此信息包含内核中的一堆地址,以十六进制表示。ksymoops 查看十六进制地址,在内核符号表(您在/proc/ksyms(Linux 2.4)或/proc/kallsyms

insmod 在加载时向 LKM 添加了一堆导出的符号。这些符号都旨在帮助 ksymoops 完成其工作。ksymoops 是一个解释和显示 "oops" 的程序。"oops" 显示是 Linux 内核在检测到内部内核错误(并因此终止进程)时显示的内容。此信息包含内核中的一堆地址,以十六进制表示。(Linux 2.6)中看到的)中查找它们,并将 oops 消息中的地址转换为符号地址,您可能可以在汇编程序列表中查找这些符号地址。因此,假设您的 LKM 崩溃了。oops 消息包含导致崩溃的指令的地址,您希望 ksymoops 告诉您的是:1) 该指令在哪个 LKM 中?2) 该指令相对于该 LKM 的汇编程序列表的位置?对于 oops 消息中的数据地址,也会出现类似的问题。.

/proc/ksyms要回答这些问题,ksymoops 必须能够从内核符号表中获取 LKM 各个节的加载点和长度。10.2. .modinfo 节好吧,在 Linux 2.4 中,insmod 知道这些地址,因此它只是为它们创建符号,并将它们包含在与 LKM 一起加载的符号中。

/proc/kallsyms特别是,这些符号被命名为(您可以通过查看

来亲自查看这一点)

__insmod_

10.4. Ksymoops 符号insmod 在加载时向 LKM 添加了一堆导出的符号。这些符号都旨在帮助 ksymoops 完成其工作。ksymoops 是一个解释和显示 "oops" 的程序。"oops" 显示是 Linux 内核在检测到内部内核错误(并因此终止进程)时显示的内容。此信息包含内核中的一堆地址,以十六进制表示。name_Ssectionname_Llength其中

insmod 在加载时向 LKM 添加了一堆导出的符号。这些符号都旨在帮助 ksymoops 完成其工作。ksymoops 是一个解释和显示 "oops" 的程序。"oops" 显示是 Linux 内核在检测到内部内核错误(并因此终止进程)时显示的内容。此信息包含内核中的一堆地址,以十六进制表示。name

_S是 LKM 名称(如您在/proc/modules__ksymtab中看到的),.

_Lsectionname

其中是节名称,例如.text.text(不要忘记前导句点)。length是节的长度,以十进制表示。符号的值当然是节的地址。132101.

Insmod 还添加了一个非常有用的符号,它告诉您 LKM 是从哪个文件加载的。该符号的名称是

__insmod_

name

_O

10.4. Ksymoops 符号insmod 在加载时向 LKM 添加了一堆导出的符号。这些符号都旨在帮助 ksymoops 完成其工作。ksymoops 是一个解释和显示 "oops" 的程序。"oops" 显示是 Linux 内核在检测到内部内核错误(并因此终止进程)时显示的内容。此信息包含内核中的一堆地址,以十六进制表示。filespec/proc/kallsyms

_M

mtime_Vversion

其中name是 LKM 名称,如上所述。系统调用中获取的信息类型,请查看.

filespecinit_module是用于标识包含 LKM 的文件的文件规范,当它被加载时。请注意,它不一定仍然在该名称下,并且可能有多个文件规范可能已被用于引用同一文件。例如,../dir1/mylkm.o系统调用中获取的信息类型,请查看/lib/dir1/mylkm.o

mtime

是该文件的修改时间,以标准 Unix 表示形式(自 1969 年以来的秒数),以十六进制表示。version告诉您构建 LKM 的内核版本级别(与

.modinfoversion要查看

节中的相同)。它是宏LINUX_VERSION_CODE在 Linux 的

linux/version.h

文件中的值。例如,

此符号的值没有意义。

在 Linux 2.6 中,它的工作方式不同。(我还没有弄清楚是如何工作的)。10.5. 其他符号insmod 添加了另一个符号,类似于 ksymoops 符号。这个符号告诉您持久性数据在 LKM 中的位置,rmmod 需要知道这一点才能保存持久性数据。

_P

10.6. 调试符号

还有另一种与 LKM 相关的符号:kallsyms 符号。这些不是导出的符号;它们不会出现在

/proc/ksyms

中。它们引用内核中不关任何人的事,除了它们所在的模块之外的地址,并且不打算被除调试器之外的任何东西引用。Kdb,Linux 内核附带的内核调试器,是 kallsyms 符号的一个用户。

kallsyms 功能适用于基础内核和 LKM。对于基础内核,您在构建基础内核时选择它,使用 CONFIG_KALLSYMS 配置选项。当您这样做时,内核包含基础内核目标文件中所有符号的 kallsyms 符号。如果您在

/proc/kallsyms

中看到符号__start___kallsyms,您就知道您的基础内核正在参与 kallsyms 功能。

对于 LKM,您在加载时决定它是否将包含 kallsyms 符号。您将 kallsyms 定义包含在您传递给系统调用的数据中,以加载 LKM。insmod 会这样做,如果 1) 您指定了.

--kallsyms