全部博文(51)
分类: LINUX
2021-09-17 16:41:46
特殊段定义在linux linker 脚本中(区别于默认的链接脚本),这些脚本存储在arch目录树下的kernel/vmlinux.lds.S,如arm64的链接脚本:linux/arch/arm64/kernel/vmlinux.lds.S
该文件使用了linux/include/asm-generic/vmlinux.lds.h头文件中的宏定义。
本文介绍一个特殊段, __ex_table, pairs of instruction pointer, 它存储了一对指令的指针。
定义如下:
#define _ASM_EXTABLE(from, to) \ " .pushsection __ex_table, \"a\"\n" \ " .align 3\n" \ " .long (" #from " - .), (" #to " - .)\n" \ " .popsection\n" |
它允许页面错误异常处理程序( page fault exception handler) 来确定异常是否是由地址“from”的内核指令引起的,如果是,跳转到地址“to” ,该地址是fixup修复代码起始地址,否则发生内核错误。
看一下内核代码中对 exception table structure的解释呃
/* * The exception table consists of pairs of relative offsets: the first * is the relative offset to an instruction that is allowed to fault, * and the second is the relative offset at which the program should * continue. No registers are modified, so it is entirely up to the * continuation code to figure out what to do. * * All the routines below use bits of fixup code that are out of line * with the main instruction path. This means when everything is well, * we don't even have to jump over them. Further, they do not intrude * on our cache or tlb entries. */ 存储了一对相对偏移地址; insn: 可能发生“fault”的指令的相对偏移; fixup: 发生“fault”后,程序继续执行的指令的相对偏移; 这期间没有寄存器发生改变;
struct exception_table_entry { int insn, fixup; };
|
怎么理解这里的相对偏移呐?是偏移到哪里呐?
涉及到两个section:.fixup 和 __ex_table;它们的定义可以从链接脚本中找到。
在 linux/arch/arm64/kernel/vmlinux.lds.S中,看到了
.fixup 定义在 .text (很熟悉),就是代码段;
RO_DATA() 从名字中可以看到这是一些只读信息,里面存放什么呐,
在 linux/include/asm-generic/vmlinux.lds.h 能找到答案。
可以看到RO_DATA宏定义中,包含了只读的__ex_table。
.fixup 与 __ex_table 如何配合使用的?
看一个代码片段:
代码片段出现了两个section,
fixup为发生异常后的修复代码;
__ex_table仅仅是存储了两个label的地址;
这段汇编的意思是,为“1:”处的指令注册了修复程序,修复程序的入口是“3:”,若是“1:”处指令发生了异常,最终会跳转到“3:”处的指令来执行。
关于语句:_ASM_EXTABLE(1b, 3b)
这里 1b, 3b是GNU语法,称为数字本地标签(numeric local label),
使用以下语法来引用numeric local label:
n{f|b}
n是数字本地标签,范围是0-99;
f 和b用来指示汇编程序向前或者向后搜索。
kernel是如何实现地址匹配以及如何跳转到修复代码段的?
在 fixup_exception可以找到答案,首先针对给定的地址来查找是否注册了异常表,若查找成功,则修改pc值进行跳转。
来解读一下查找exception table的过程,可以看到计算__ex_table表项对应指令地址的过程用到了偏移地址,上面提到了_ASM_EXTABLE(1b, 3b)语句存储了两个label,经过链接程序处理,存储的值最终被替换为__ex_table表项相距label的偏移地址。
再次梳理一下,
某一条指令可能会引发异常,但是我们还想挽救一下,试图去修复看看,so,将目标指令的地址和修复代码的首地址放在__ex_table段中。 修复代码单独存放在名为fixup的段中。
kernel运行时,真的发生了异常,handler就去查找__ex_table段,看是否针对当前发生异常的指令注册了fixup 处理程序;若找到了就尝试运行。虽然可能还是失败