本文基于Linux-4.19.125, ARM V7,dual core。
1 code 段
Linux的code段(或者说text段)自_stext开始,到_etext结束,这段内容一般情况下是只读的,在理论上来说,这段数据在设备上应该和kernel image中的内容完全一致。
symbol___|type_______|address____________|
_stext |(char [0]) | P:C0008240
_etext | (char [0]) | P:C02CA9A0
但在实测中发现,从设备中dump出来的这段数据,与kernel image中的数据存在多处不一致的情况:
2 数据对比
随机取几个差异点:
序号 | 虚拟地址 | vmlinux内容 | 板端内容 |
---|---|---|---|
1 | 0xc0017f70 | 0x4001 | 0x4000 |
2 | 0xc00d2154 | 0x4301 | 0x4300 |
3 | 0xc00e895e | 0x4001 | 0x4000 |
4 | 0xc02852ca | 0x4101 | 0x4100 |
通过trace32对比设备侧与vmlinux中的数据差异:
3. 数据何时被修改的
41 load boot image阶段
为方便调试,修改Linux内核源码arch/arm/kernel/head.S文件如下,让CPU停在SPL跳转到内核的第一条指令:
上电,CPU停止在0x40008000地址:
此时dump, 将_stext和_etext转换成物理地址分别是0x40008240和:0x402CA9A0
执行d.save.Binary text_dump_start.bin eaxi:0x40008240--eaxi:0x402CA9A0得到 text_dump_start.bin
对比text_dump_start.bin和text_vmlinux.bin文件发现二者完全一致,说明SPL加载boot image到内核过程中并没有修改code段内容。
3.2 __mmap_switched 阶段
__mmap_switched是开启mmu后的第一条语句,经过验证发现,进入到__mmap_switched时,监控的上述4个随机抽样点的数据已经被修改了,这说明修改动作发生在__mmap_switched之前。
3.3 __fixup_pv_table阶段
经过调试发现,上述监控的4个随机抽样点的数据在__fixup_pv_table中被修改了。
__fixup_pv_table是在CONFIG_ARM_PATCH_PHYS_VIRT内核配置项打开的情况下才支持的,关于CONFIG_ARM_PATCH_PHYS_VIRT的作用,概括来说就是:
开发人员需要让内核在不重新编译的情况下,在不同内存配置的系统中能够正常运行。
内核可能被编译成在特定的虚拟地址(如 0xC0000000 )处执行,但实际可能被加载到 物理地址0x10000000、0x40000000或其他地址。
CONFIG_ARM_PATCH_PHYS_VIRT的作用就是实现同一kernel image被加载到不同物理地址后仍然能够正常运行这一目的的。
__fixup_pv_table的实现很复杂,其核心思想:
每当在内核中调用 __virt_to_phys() 或 __phys_to_virt() 时,被替换成一段内联汇编代码(位于arch/arm/include/asm/memory.h)
然后连接器就会将section 切换到一个名为 .pv_table 的section 上,然后在该section 中添加一个指针,指向刚刚添加的汇编指令的位置
这就是说,.pv_table 接会扩展成一个指针的表格,指向所有这些内联汇编代码所在的位置。
在__fixup_pv_table过程中,会遍历整个pv_table 表格,取出每一个指针,检查指针所指位置的指令,然后利用物理和虚拟内存之间的偏移量对这些指令打补丁。
4 检验
根据我们前面随机取的几4个取样点:
序号 | 虚拟地址 | vmlinux内容 | 板端内容 |
---|---|---|---|
1 | 0xc0017f70 | 0x4001 | 0x4000 |
2 | 0xc00d2154 | 0x4301 | 0x4300 |
3 | 0xc00e895e | 0x4001 | 0x4000 |
4 | 0xc02852ca | 0x4101 | 0x4100 |
0xc0017f70
对应代码:
0xc00d2154
对应代码:
0xc00e895e
0xc02852ca
以上4个随机取样点,均验证被修改的代码确实都与pv_table有关,可以证实这些被修改的内容是在__fixup_pv_table过程中修改的。
5 参考文档
- How the ARM32 kernel starts — linusw
- 万字长文揭秘 ARM 32 内核是如何启动的!