【发布时间】:2021-04-18 10:24:21
【问题描述】:
我正在尝试验证和理解在模拟框架中执行的指令。 仿真步骤如下:
- 二进制文件在 x86 主机中使用 gcc(带有 -fPIC 标志)进行交叉编译
- 然后在名为 SimNow 的虚拟机 x86 中移动并执行二进制文件(AMD 使用该虚拟机测试其处理器)
- SimNow 机器生成一个已执行指令列表,这些指令被传递给框架,其中包括有关每条指令的信息:虚拟地址、物理地址、大小、操作码。
- 借助 x86 反汇编程序(名为 distorm),框架生成执行指令的跟踪,包括助记符和操作数。 这是跟踪输出的示例:
已执行指令列表包括二进制中包含的指令和可能的内核指令。
我正在使用二进制文件上 objdump 的输出来验证跟踪的用户指令。它们相等,证实了执行的正确性。
这是上图中指令的objdump输出:
对于内核指令,我必须应用初步步骤:
- 我将内核头文件安装到虚拟机中,并提取了用于在其上执行 objdump 的 linux 映像。
- 我通过将内核指令的虚拟地址与 /proc/kallsysms 中包含的虚拟地址进行比较,将内核符号添加到跟踪输出中。
对于验证步骤,我使用与用户说明相同的方法,将 linux 内核映像的 objdump 与跟踪输出进行比较。 但是,我注意到了一些差异……主要是在发现内核符号指令时。 这是跟踪的输出:
这是linux内核镜像的对应部分:
从这些图片中可以看出,每个 callq 对应一个内核符号(将 linux 映像的虚拟地址与 /proc/kallsyms 进行比较)在跟踪输出中被替换为 NOP DWORD(nopl 指令)。
我想要做的是了解为什么内核符号使用 NOP DWORD 而不是 callq。
是搬迁造成的吗?如果是,我该如何重建此类指令的重定位?
注意:我使用 -dr 执行了 objdump 以检查 Linux 映像上的重定位,但输出没有改变。
我的内核指令验证方法不对?
【问题讨论】:
-
参见elf(5) 并使用objdump(1) 和readelf(1)。使用最近的GCC 编译您的
foo.c和gcc -Wall -fverbose-asm -O2 -S foo.c· 并使用less(1) 查看foo.s内部。另请阅读Linkers and loaders -
我在二进制文件上使用了 elf、objdump 和 readelf,它适用于用户指令。但是,它不适用于内核指令,它不应该包含在二进制文件中......不是吗?
-
这些可能是内核在加载后动态修改的内容的占位符吗?例如将它们排除在外,或将它们从 NOP 更改为 callq,以实现非常低的开销跟踪? Linux 确实有一些 inline-asm hack 可以生成符号以对其他事物进行自我修改,例如如果 SMP 内核在 UP 机器上启动,则 NOPing
lock前缀。而且我想我已经阅读了有关使用低开销跟踪点或其他东西的代码修改的Linux。所以这可以解释为什么内核在磁盘上的 ELF 映像与内存中的执行不匹配。 -
是的,这看起来像是一个函数的最顶层(在制作堆栈帧之前),这听起来像是一个可能找到跟踪调用的地方(对保留所有寄存器的特殊函数)。出于某种原因 NOP 调用可以解释为什么 NOP 上有一个调用目标的符号。
-
这些看起来像
ftrace自我修改代码。见arch/x86/kernel/ftrace.c
标签: assembly gcc linux-kernel x86 trace