考虑到指令具有不同的长度,CPU 如何知道下一条指令应该读取多少字节?

2024-03-01

所以我正在读一篇论文,其中他们说静态反汇编二进制代码是不可判定的,因为一系列字节可以用多种可能的方式表示,如图所示(其 x86 )

所以我的问题是:

  1. 那么CPU是如何执行这个的呢?例如,在图中,当我们到达 C3 之后时,它如何知道下一条指令应该读取多少字节?

  2. CPU如何知道执行一条指令后应该将PC增加多少?它是否以某种方式存储当前指令的大小,并在想要增加 PC 时添加该大小?

  3. 如果CPU能够以某种方式知道它应该为下一条指令读取多少字节或者基本上如何解释下一条指令,为什么我们不能静态地做到这一点?


简单的方法是只读取一个字节,对其进行解码,然后确定它是否是一条完整的指令。如果没有读取另一个字节,则在必要时对其进行解码,然后确定是否已读取完整的指令。如果不继续读取/解码字节,直到读取完整的指令。

这意味着如果指令指针指向给定的字节序列,则只有可能的方法来解码该字节序列的第一条指令。之所以出现歧义,是因为要执行的下一条指令可能不位于紧随第一条指令之后的字节处。这是因为字节序列中的第一条指令可能会更改指令指针,因此除了后续指令之外的其他指令也会被执行。

RET(retn)示例中的指令可能是函数的结尾。函数通常以 RET 指令结尾,但不一定如此。一个函数可能有多个 RET 指令,但这些指令都不在函数末尾。相反,最后一条指令将是某种 JMP 指令,它跳回到函数中的某个位置,或者完全跳回另一个函数。

这意味着在您的示例代码中,如果没有更多上下文,就不可能知道 RET 指令后面的任何字节是否会被执行,如果是的话,哪个字节将是以下函数的第一条指令。函数之间可能有数据,或者该 RET 指令可能是程序中最后一个函数的结尾。


x86 指令集尤其具有相当复杂的格式,其中包括可选前缀字节、一个或多个操作码字节、一两个可能的寻址形式字节,以及可能的位移和立即字节。前缀字节可以添加到几乎任何指令的前面。操作码字节决定有多少操作码字节以及指令是否可以具有操作数字节和立即数字节。操作码还可能表明存在位移字节。第一个操作数字节确定是否存在第二个操作数字节以及是否存在位移字节。

Intel 64 和 IA-32 架构软件开发人员手册中有下图显示了 x86 指令的格式:

用于解码 x86 指令的类似 Python 的伪代码如下所示:

# read possible prefixes

prefixes = []
while is_prefix(memory[IP]):
    prefixes.append(memory[IP))
    IP += 1

# read the opcode 

opcode = [memory[IP]]
IP += 1
while not is_opcode_complete(opcode):
    opcode.append(memory[IP])
    IP += 1

# read addressing form bytes, if any

modrm = None
addressing_form = []    
if opcode_has_modrm_byte(opcode):
    modrm = memory[IP]
    IP += 1
    if modrm_has_sib_byte(modrm):
        addressing_form = [modrm, memory[IP]]
        IP += 1
    else:
        addressing_form = [modrm]

# read displacement bytes, if any

displacement = []
if (opcode_has_displacement_bytes(opcode)
    or modrm_has_displacement_bytes(modrm)):
    length = determine_displacement_length(prefixes, opcode, modrm)
    displacement = memory[IP : IP + length]
    IP += length

# read immediate bytes, if any

immediate = []
if opcode_has_immediate_bytes(opcode):
    length = determine_immediate_length(prefixes, opcode)
    immediate = memory[IP : IP + length]
    IP += length

# the full instruction

instruction = prefixes + opcode + addressing_form + displacement + immediate

上述伪代码遗漏的一个重要细节是指令长度限制为 15 个字节。可以构造 16 字节或更长的有效 x86 指令,但此类指令在执行时将生成未定义操作码 CPU 异常。 (我还遗漏了其他细节,例如如何在 Mod R/M 字节内部编码操作码的一部分,但我认为这不会影响指令的长度。)


然而,x86 CPU 实际上并不像我上面描述的那样解码指令,它们只是解码指令,就好像它们一次读取每个字节一样。相反,现代 CPU 会将整个 15 个字节读入缓冲区,然后并行解码字节(通常在一个周期内)。当它完全解码指令、确定其长度并准备好读取下一条指令时,它会转移缓冲区中不属于指令的剩余字节。然后,它读取更多字节以再次将缓冲区填充到 15 个字节,并开始解码下一条指令。

现代 CPU 会做的另一件事是推测性地执行指令,这不是我上面所写的内容所暗示的。这意味着 CPU 将解码指令并尝试在执行完前面的指令之前尝试执行它们。这反过来意味着 CPU 可能最终会解码 RET 指令之后的指令,但前提是它无法确定 RET 将返回到哪里。由于尝试解码和暂时执行不打算执行的随机数据可能会导致性能下降,因此编译器通常不会在函数之间放置数据。尽管他们可能会用永远不会执行的 NOP 指令填充此空间,以便出于性能原因对齐函数。

(他们很久以前就在函数之间放置只读数据,但这是在可以推测执行指令的 x86 CPU 变得普遍之前。)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

考虑到指令具有不同的长度,CPU 如何知道下一条指令应该读取多少字节? 的相关文章

随机推荐

  • 显示喜欢项目列表中的项目的用户

    我认为这是一个不切实际的问题 但我想得到一些反馈来确认 我有一个产品和用户数据库 用户可以在其中喜欢产品 喜欢的数据存储在仅包含 pid 和 uid 的参考表中 客户请求是显示喜欢产品列表中每个产品的 3 位用户 问题是 不可能在产品列表的
  • Chrome/Webkit 中的 CSS 动画/缩放问题

    此效果在 FF 中运行良好 但在 Chrome 中则不然 Chrome 中的 Firebug 结果显示 webkit animation 未在 Chrome 中呈现 然而 在 Firefox 中 您会在对象入口处看到 拉伸 效果 在 Chr
  • 使用 ICS 更改 wifi 网络时,ErrnoException:isConnected 失败:EHOSTUNREACH(没有到主机的路由)

    在 ics 上使用我的应用程序时 将 wifi 网络从网络更改为网络后 所有图像请求都开始返回 但出现异常 异常失败 gt org apache http conn HttpHostConnectException Connection t
  • 使用 Scanner 的 nextLine() 和 hasNextLine() 方法时出现问题

    我有一个包含以下数据的日志文件 最短路径 2 RV3280 RV0973C RV2888C最短路径 1 RV3280 RV2502C最短路径 2 RV3280 RV2501C RV1263最短路径 2 RV2363 Rv3285 RV328
  • Windows 服务如何以标准时间间隔启动应用程序?

    顾问设置 Windows 服务来运行应用程序 该应用程序应该每 15 分钟运行一次 该应用程序根本没有运行 并且服务似乎运行良好 我不熟悉应用程序作为服务运行时如何以标准间隔运行 该服务使用 SRVANY EXE 工具 任何使用 SRVAN
  • mysql删除重复数据

    这向我显示了所有具有完全相同的两个条目的名字和姓氏 SELECT firstname lastname COUNT AS Count FROM people GROUP BY firstname lastname HAVING Count
  • 尝试运行 Strapi 会出现模块解析错误

    我正在尝试通过 Ubuntu 20 04 上的 npm 使用 npm rundevelop 运行一个 Strapi 项目 该代码来自我的一个小组项目 没有其他人遇到这些问题 Error cache plugins strapi plugin
  • 带有 goto 命令的 Windows 批处理文件不起作用

    我对 GOTO 命令和附属标签有疑问 事实 给定文件夹中的一堆文件 它们是日志错误 我需要打开它们并检查它们是否包含特定字符串 如果是 则从文件名中删除一些字符 最后一次出现 之后的所有字符 包括其本身 并执行其他操作 为了切断字符 我以循
  • F# 缺少类型约束

    在以下代码中 请注意 get Zero 的类型约束 type Wrapper lt t gt Data t let compute lt t when t static member get Zero unit gt t and t sta
  • nodejs Route.get() 需要回调函数但得到一个 [object String]

    我开始使用带有express的nodejs进行编码 所以我在我的文件 test js 中执行了此操作 该文件位于我的文件夹中 const express require express const router new express Ro
  • 每组查询的前 3 个值 MS Access

    我是 MS Access 的新手 我正在尝试进行一个查询 该查询将根据分数提取 3 个不同类别中的前 3 名人员 即期望的结果是 Child s name Membership Type Total Points Jon Snow Seni
  • 无法在特定 div 上滚动来触发 jquery 函数

    简短版本 这有效 document on click Container function 这不会 document on scroll Container function 长版 很抱歉 发布代码片段是不可行的 因为它是一个复杂的类似应用
  • 类中的部分评估类型

    这是我提出的问题的具体版本here https stackoverflow com questions 60072003 reordering type parameters in haskell 我有一个算法 可以产生一些输出 并且可以产
  • 使用鼠标选择要捕获的区域

    我正在制作一个基于Java的屏幕截图应用程序 当您按下键盘上的组合键时 我想这样做这个视频 http www youtube com watch v bJ6VbbpQ0XY发生在您在屏幕上选择区域的位置 并且它会拍摄所选区域的屏幕截图 如何
  • Google 搜索查询中参数的含义? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 有没有关于 Google 查询中的参数含义的资源 有没有分析过 Google 搜索页面的内部运作方式 例子是 http www googl
  • 如何使用eclipse进行C#开发? [复制]

    这个问题在这里已经有答案了 可以使用eclipse进行C 开发吗 如果可以的话 该怎么办呢 您最喜欢的组合是什么 虽然我见过一些半成品的插件 但我不相信有任何东西可以做到这一点close到 Eclipse 中的 Java 工具 如果您使用的
  • 使用 jquery-1.4.1.js 访问被拒绝

    我正在使用 VS2010 和 jquery 1 4 1 js 版本 我尝试访问 WCF 服务 但出现奇怪的错误 访问被拒绝 in jquery 1 4 1 js在页码处4982 jquery 1 4 1 js Open the socket
  • 清除控制台缓冲区

    我正在 VS2008 中编写一个示例控制台应用程序 现在我有一个Console WriteLine 在屏幕上显示输出的方法 然后有Console ReadKey 等待用户结束应用程序 If I press Enter while the C
  • 如何从服务类调用组件方法 - Angular

    我试图从服务类调用组件方法 但收到类似 错误类型错误 无法读取未定义的属性 测试 的错误 但是我遇到了类似的问题 但主要解释了组件到组件的调用 所以我没有正确理解 例子 测试组件 ts Component selector componen
  • 考虑到指令具有不同的长度,CPU 如何知道下一条指令应该读取多少字节?

    所以我正在读一篇论文 其中他们说静态反汇编二进制代码是不可判定的 因为一系列字节可以用多种可能的方式表示 如图所示 其 x86 所以我的问题是 那么CPU是如何执行这个的呢 例如 在图中 当我们到达 C3 之后时 它如何知道下一条指令应该读