CPU/汇编器如何知道下一条指令的大小?

6
举个例子,假设我正在构建一台虚拟机。我有一个字节数组和一个while循环,那么我如何知道从字节数组中读取多少字节以解释下一条Intel-8086式的指令?

编辑:(评论)

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

编辑:

在这篇关于http://www.swansontec.com/sintel.html的文章中,我自己找到了答案。

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


每个指令的长度不是固定的吗?我怎么知道下一条指令是F还是FF?编辑:修改了问题,已找到答案。 - Ashley Meah
1
抱歉,我无法比这更好地解释。我没有写每个指令都有相同的长度,而是写它们有固定的长度。例如,一个长跳转始终是5个字节长,加法可能是2个字节长等等。通过操作码,人们可以知道应该读取多少附加字节。 - The Paramagnetic Croissant
可能是与 获取汇编指令的大小 相似的问题。 - Alexis Wilke
带有可变长度指令的计算机如何知道正在获取的指令的长度?指令长度可变时的指令解码。 - phuclv
可能是 指令长度可变时的指令解码 的重复问题。 - phuclv
显示剩余4条评论
2个回答

8
CPU仅仅解码指令。在8086的情况下,第一个字节告诉处理器需要获取多少个字节。它不一定是第一个字节,但第一个字节必须以某种方式指示需要获取更多,这些更多可以表示您需要更多。对于像x86家族这样的8位指令集,您从一个字节开始,然后查看需要多少个字节,并且由于未对齐,您必须将指令流视为字节流才能解码它。
您应该编写一个非常简单的指令集模拟器,只有几个指令,足以加载一个寄存器,将某些内容添加到其中,然后循环。这对于您所要理解的内容非常有教育意义,并且编写可能只需要半小时左右。

这不是一个答案,但我已经自己解决了,我在你发帖之前编辑了问题,所以请大家在发帖前仔细阅读。 - Ashley Meah
既然这个问题已经被问过和回答了很多次,也许你应该删除这个问题或者请求管理员将其删除。 - old_timer
其他评论是获取汇编指令的大小,其中您知道完整的指令。例如 mov ax,al。 - Ashley Meah
我的问题是在不知道完整指令的情况下获取内存中下一条指令的大小。第一个指令字节中的1个字节仅为7位,最后一位告诉CPU它是字还是字节。如果在受支持的指令集中支持更长的指令,则我猜测下一个字节也使用相同的方法。这是英特尔,所以AMD可能会有所不同。http://umcs.maine.edu/~cmeadow/courses/cos335/8086-instformat.pdf - Ashley Meah
1
有时候需要查看第二个字节来确定是否还有更多的内容以及数量。这里没有什么特别的,这就是处理器工作的方式,特别是可变长度的CISC类型...可以将8086替换为6502或z80或其他长列表中的任何一个,并且如何解码或处理器如何解码在各种供应商文档中都有说明。 - old_timer
显示剩余4条评论

7

简述:

解决方案不仅是一个固定大小的数组。


这一切都取决于上下文,这就是为什么像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个NOP(无操作)并跳到它后面的下一条指令(不会将eax设置为0x90909090)。这是因为NOP是一个由0x90组成的1字节指令。

我已经自己解决了,而你是错的。你过于思考并忽略了CPU知道要读取多少字节的关键原因。对于一个字节指令,有一个特定的位告诉CPU将下一个字节作为指令的一部分读取。 - Ashley Meah
4
我可能在考虑你的应用程序时想太多了,但我所描述的一些陷阱并不是“错误的”。跳转到指令中间的地址是恶意软件作者和混淆器使用的策略。这绝对会使线性分析引擎失效。以下是关于如何实现此功能的算法讨论:http://resources.infosecinstitute.com/linear-sweep-vs-recursive-disassembling-algorithm/。除此之外,我看了你的参考文献(http://www.swansontec.com/sintel.html),它很好,正如你所说,解决了你的VM应用程序问题。 - XlogicX
TLDR:解决方案比固定大小数组更复杂。这一切都与上下文有关,这就是为什么像IDA这样的反汇编器有复杂的算法来完成这项工作。对于x86,指令长度是可变的。但是,如果您知道指令的起始位置,则可以知道该指令的结束位置。因此,您可能会知道下一个指令从哪里开始。我是新手,我有借口。然而,你没有——确定大小非常重要,也不难,你已经在软件中生活了太久。你可能知道软件,但不知道硬件如何理解它,这同样重要甚至更重要。 - Ashley Meah

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接