【问题标题】:How does the CPU/assembler know the size of the next instruction?CPU/汇编器如何知道下一条指令的大小?
【发布时间】:2014-09-25 22:34:56
【问题描述】:

为了举例,假设我正在构建一个虚拟机。我有一个字节数组和一个 while 循环,我如何知道要从字节数组中读取多少字节,以便下一条指令解释类似 Intel-8086 的指令?

编辑:(评论)

CPU 在指令指针处读取操作码,使用 8086 和 CISC 你有一个字节和两个字节的指令。如何知道下一条指令是 F 还是 FF?

编辑:

我自己在http://www.swansontec.com/sintel.html的这段文字中找到了答案

操作码或操作码位于任何可选前缀之后。操作码告诉处理器要执行哪条指令。此外,操作码包含描述预期操作数大小和类型的位字段。例如,NOT 指令的操作码为 1111011w。在此操作码中,w 位确定操作数是字节还是字。 OR 指令的操作码为 000010dw。在此操作码中,d 位确定哪些操作数是源和目标,而 w 位再次确定大小。一些指令有几个不同的操作码。例如,当 OR 与累加器寄存器(AX 或 EAX)和一个常数一起使用时,它具有特殊的节省空间的操作码 0000110w,从而无需单独的 ModR/M 字节。从大小编码的角度来看,不需要记住精确的操作码位。对特定指令可用的操作码类型有一个大致的了解更为重要。

【问题讨论】:

  • “中断指令”是什么意思?你知道“中断”这个词是什么意思吗?至于最初的问题:CPU 知道一条指令有多长,因为该信息要么被烧录到它的硬件中,要么被编程到它的微码中。
  • cpu在指令指针处读取操作码,8086和CISC你有一个字节和两个字节的指令。我如何知道下一条指令是 F 还是 FF?编辑:对不起,意思是解释
  • 它知道,因为每条指令都有固定的长度。如果指令是长跳转,它知道它必须另外读取接下来的 4 个字节。如果是加法,它知道它只需要读取下一个字节。等等等等。
  • 每条指令不是固定长度吗?我如何知道下一条指令是 F 还是 FF?编辑:已编辑的问题,发现 ansew
  • 对不起,我无法比这更好地解释它。我没有写到每条指令都有相同的长度。我写道,它们的长度是固定的。例如。一个长跳转总是 5 个字节长,一个加法可能是 2 个字节长,等等。可以从操作码中知道应该读取多少个额外字节。

标签: assembly x86 cpu instruction-set


【解决方案1】:

cpu 只是简单地解码指令。在 8086 的情况下,第一个字节告诉处理器还要得到多少。它不一定是第一个字节,第一个字节确实必须以某种方式表明您需要获得更多,更多可以表明您需要更多。对于像 x86 系列这样的 8 位指令集,您从一个字节开始,然后查看您还需要多少,并且还没有对齐,您必须将指令流视为字节流才能对其进行解码。

您应该为自己编写一个非常简单的指令集模拟器,只有少量指令,可能足以加载一个寄存器,添加一些内容然后循环。对你想要理解的内容非常有教育意义,如果写出来可能需要半个小时。

【讨论】:

  • 这不是一个问题,但我自己已经解决了,我在您发布之前编辑了问题,所以人们请在发布前实际阅读。
  • 既然这个问题已经被问了很多次了,也许你应该删除这个问题或者让版主删除它。
  • 其他 cmets 正在获取您知道完整指令的汇编指令的大小。例如 mov ax, al
  • 我的问题是在不知道完整指令是什么的情况下获取内存中下一条指令的大小。 1字节是第一个指令字节只有7位,最后一位告诉CPU它是一个字还是一个字节。如果受尊重的指令集支持,我猜同样的东西用于更长的指令的下一个字节。这是英特尔,所以 AMD 可能会有所不同。 umcs.maine.edu/~cmeadow/courses/cos335/8086-instformat.pdf
  • 有时它必须查看第二个字节以确定是否有更多以及有多少。这里没什么特别的,这是处理器如何在特定的可变长度 cisc 类型中工作......可以用 6502 或 z80 或其他一长串列表替换 8086,以及如何解码或处理器如何解码在各种供应商文档。
【解决方案2】:

TLDR:

解决方案比固定大小的数组更复杂。


一切都与上下文有关,这就是为什么像 IDA 这样的反汇编程序有复杂的算法来执行此操作。

对于 x86,指令是可变长度的。但是如果你知道一条指令的开始,你就会知道这条指令在哪里结束。正因为如此,你可能知道下一个从哪里开始。我将很快解释例外情况。但首先,这里有一个例子:

ASM:
mov eax, 0
xor eax, eax

Machine:
b8 00 00 00 00
31 c0

说明:

移动到 eax 是 B8,后跟一个 32 位(4 字节)值移动到 eax(因为 eax 是 32 位)。换句话说,mov eax, immediate 将始终为 5 个字节。因此,如果您知道您从一条指令开始(并不总是一个安全的假设),并且字节是 B8,那么您就知道它是一条 5 字节指令,并且下一条指令应该在 5 字节之后开始。

请注意,两条指令(mov eax, 0xor eax, eax)实际上执行相同的操作,将 eax 清除为 0。

例外:

跳跃/呼叫可能会让事情变得棘手。可以跳转到位于“指令中间”的地址空间...但仍会执行。

让我们看看:

mov eax, 0x90909090

机器码:

b8 90 90 90 90

如果我们稍后有一条 jmp 指令跳转到上述指令的第 3 个字节的地址(在它的中间某处),它只会执行 3 个 NOP(无操作)并落到它之后的下一条指令(不将 eax 设置为 0x90909090)。这是因为NOP 是由 0x90 组成的 1 字节指令。

【讨论】:

  • 我自己已经解决了,你错了。您过度思考并错过了 CPU 知道要读取多少字节的关键原因。对于一个字节的指令,有一个特定的位告诉 CPU 读取下一个字节作为指令的一部分。
  • 我可能在您的应用程序的上下文中思考过度,但我描述的一些陷阱并不是“错误的”。跳转到指令中间的地址是恶意软件作者和混淆者将使用的策略。这绝对会通过一个线性分析引擎关闭。这是关于执行此操作的算法的讨论:resources.infosecinstitute.com/…。除此之外,我阅读了您的参考资料 (swansontec.com/sintel.html),它非常好,并且正如您所说,回答/解决了您的 VM 应用程序问题。
  • 引用“TLDR:解决方案比固定大小的数组更复杂。这完全取决于上下文,这就是为什么像 IDA 这样的反汇编程序有复杂的算法来执行此操作。对于 x86,指令是可变长度的。但是如果你知道一个指令的开始,你知道那个指令在哪里结束。因此,你可能知道下一个指令从哪里开始“我是新手,我有借口。然而你不知道——要弄清楚大小是非常重要而不是难的,你在软件中生活的时间太长了。您可能知道软件,但不知道硬件如何理解它的重要性。
猜你喜欢
  • 1970-01-01
  • 2011-04-28
  • 2011-05-24
  • 2021-06-25
  • 2014-09-19
  • 2017-05-28
  • 2010-10-16
  • 1970-01-01
相关资源
最近更新 更多