指令解码器如何区分前缀和主操作码?

2023-12-25

我正在尝试了解 x86 指令编码格式。我读过的所有资料仍然让这个主题令人困惑。我开始有点了解它,但我无法理解的一件事是 CPU 指令解码器如何区分操作码前缀和操作码。

我知道指令的整个格式基本上取决于操作码(当然还有操作码中定义的额外位字段)。有时指令没有前缀,操作码是第一个字节。解码器怎么知道?

我假设指令解码器能够区分差异,因为操作码字节和前缀字节不会共享相同的二进制值。因此解码器可以判断字节中唯一的二进制数是指令还是前缀。例如(在这个例子中我们将坚持使用单字节操作码)REX or LOCK前缀不会与架构指令集中的任何操作码共享相同的字节值。


传统(单字节)前缀与您所说的操作码字节不同,因此状态机只能记住它看到的前缀,直到它到达操作码字节。

The 0f2 字节操作码的转义字节并不是真正的前缀。它必须与第二个操作码字节连续。因此,遵循0f, anybyte 是一个操作码,即使它类似于f2否则这将是一个前缀。 (这也适用于以下0f 3a or 0f 38SSSE3 及更高版本的 2 字节转义,或编码这些转义序列之一的 VEX/EVEX 前缀)。

如果您查看操作码映射,就会发现单字节前缀和操作码之间没有任何不明确的条目。 (例如。http://ref.x86asm.net/coder64.html http://ref.x86asm.net/coder64.html,并注意 2 字节 0F .. 操作码是如何单独列出的)。


解码器必须知道当前的模式(以及其他事情);例如 x86-64 删除了 1 字节inc/dec reg用作 REX 前缀的操作码。 (x86-x64 中不同或完全删除的 x86 32 位操作码 https://stackoverflow.com/questions/32868293/x86-32-bit-opcodes-that-differ-in-x86-x64-or-entirely-removed)。我们甚至可以利用这种差异来编写多语言机器代码,这些代码在解码时运行方式不同32 位与 64 位模式 https://stackoverflow.com/questions/38063529/x86-32-x86-64-polyglot-machine-code-fragment-that-detects-64bit-mode-at-run-ti, 甚至区分所有 3 种模式大小 https://codegolf.stackexchange.com/questions/139243/determine-your-languages-version/139717#139717.

x86 机器码是一个字节流not自同步(例如,ModRM 或立即数可以是任何字节)。 CPU 始终知道从哪里开始解码,要么是跳转目标,要么是上一条指令结束后的字节。这是指令的开始(包括前缀)。

内存中的字节只是字节,只有被CPU解码后才成为指令。 (虽然在正常程序中,只需从顶部反汇编.text部分does给您程序的说明。自修改和混淆代码是不正常的。)

AVX / AVX-512:与操作码重叠的多字节前缀

多字节 VEX 和 EVEX 前缀在 32 位模式下并不那么简单。例如,VEX 前缀与 64 位以外的模式下的 LES 和 LDS 的无效编码重叠。 (这c4 and c5LES 和 LDS 的操作码在 64 位模式下始终无效,VEX 前缀除外。)https://wiki.osdev.org/X86-64_Instruction_Encoding#VEX.2FXOP_opcodes https://wiki.osdev.org/X86-64_Instruction_Encoding#VEX.2FXOP_opcodes

在传统/兼容模式下,当 AVX(VEX 前缀)和 AVX-512(EVEX 前缀)时,没有任何剩余字节不是操作码或前缀,因此唯一的扩展空间是作为操作码的编码,这些操作码是仅对有限的 ModRM 字节集有效。 (例如 LES / LDS 需要内存源,而不是寄存器 - 这就是为什么 VEX 前缀中的某些位被反转,因此字节的前 2 位c4 or c5一直会1在 32 位模式下而不是0。 这是 ModRM 中的“模式”字段,并且11表示注册)。

(有趣的事实:VEX 前缀在 16 位实模式下无法识别,显然是因为某些软件使用了与 LES / LDS 相同的无效编码作为故意陷阱,需要在 #UD 异常处理程序中进行整理。VEX 前缀are以 16 位识别受保护的不过,模式。)


AMD64 通过删除 AAM 等指令以及 LES/LDS(以及一字节inc/dec reg用作 REX 前缀的编码),但 CPU 供应商继续关心 32 位模式,并且没有添加任何仅在 64 位模式下可用的扩展,这些扩展可以简单地利用这些免费的操作码字节。这意味着要找到方法将新的指令编码塞进 32 位机器代码中越来越小的间隙中。 (通常通过强制前缀,例如rep bsr = lzcnt在具有该功能的 CPU 上,会产生不同的结果。)

所以现代 CPU 中的解码器支持 AVX / BMI1/2 的设备必须查看多个字节来决定这是否是有效 AVX 或其他 VEX 编码指令的前缀,或者在 32 位模式下是否应解码为 LES 或 LDS。 (我想看看指令的其余部分来决定是否应该#UD)。

但现代 CPU 无论如何都会一次查看 16 或 32 字节以并行查找指令边界。 (然后将这些指令字节组馈送到实际解码器,再次并行。)https://www.realworldtech.com/sandy-bridge/4/ https://www.realworldtech.com/sandy-bridge/4/

所使用的前缀方案也是如此AMD XOP https://en.wikipedia.org/wiki/VEX_prefix#History,这很像 VEX。

阿格纳·福格的博客文章停止指令集战争 https://www.agner.org/optimize/blog/read.php?i=25从 2009 年开始(AVX 宣布后不久,在第一个支持它的硬件之前)有一个用于未来扩展的剩余未使用编码空间表,以及一些关于它被“分配”给 AMD、Intel 或 Via 的注释。

相关/示例

  • 如何判断x86指令的长度? https://stackoverflow.com/questions/4567903/how-to-tell-the-length-of-an-x86-instruction(包括我的答案)有一些关于 x86 机器代码的更多细节。
  • https://codegolf.stackexchange.com/questions/133486/find-an-illegal-string/133622#133622 https://codegolf.stackexchange.com/questions/133486/find-an-illegal-string/133622#133622(在 codegolf.SE 上 - 最短的字节序列,如果不跳过,肯定会 #UD 错误。它必须足够长,以免被 CPU 用作立即执行mov r64, imm64例如。)
  • 为什么 gdb 上的 x/i 给出不同的结果然后反汇编? https://stackoverflow.com/questions/48739930/why-does-x-i-on-gdb-give-different-results-then-disassemble- 在错误的位置开始解码并将另一条指令的中间解码为其他内容的示例。

机器代码技巧:以多种方式解码同一字节

(这是not确实与前缀相关,但总的来说,了解规则如何应用于奇怪的情况可以帮助准确理解事情的工作原理。)

软件反汇编程序确实需要知道一个起点。如果混淆的代码混合了代码和数据,并且如果您只是假设可以按顺序解码而不需要后续跳转,那么实际执行会跳转到您无法到达的位置,这可能会出现问题。

幸运的是编译器生成的代码不会这样做 https://stackoverflow.com/questions/55607052/why-do-compilers-put-data-inside-textcode-section-of-the-pe-and-elf-files-and/55609077#55609077如此幼稚的静态反汇编(例如通过objdump -d or ndisasm,而不是 IDA)找到与实际运行程序相同的指令边界。

这对于running混淆的机器代码; CPU 只是按照它的指示去做,从不关心你告诉它跳转到的位置之前的字节。拆解without运行/单步执行程序是一件困难的事情,特别是有可能自我修改代码并跳转到天真的反汇编程序认为是早期指令中间的地方。

混淆的机器代码甚至可以以一种方式解码指令,然后跳回到该指令的中间,以便后面的字节成为操作码(或前缀+操作码)。如果您这样做,具有 uop 缓存或在 I 缓存中标记指令边界的现代 CPU 运行速度会很慢(但正确),因此这更像是一种有趣的代码高尔夫技巧(以牺牲速度为代价的极端代码大小优化)或混淆技术。

有关此示例,请参阅我的 codegolf.SE x86 机器代码答案使用自定义斐波那契数列打高尔夫球 https://codegolf.stackexchange.com/questions/121118/golf-a-custom-fibonacci-sequence/211331#211331。我将摘录与 CPU 在循环返回后看到的内容一致的反汇编内容cfib.loop,但请注意,第一次迭代的解码方式不同。因此,我在循环外仅使用 1 个字节而不是 2 个字节,以有效地跳转到中间以开始第一次迭代。有关完整说明和其他反汇编,请参阅链接的答案。

0000000000401070 <cfib>:
  401070:       eb                      .byte 0xeb      # jmp rel8 consuming the 01 add opcode as a rel8
0000000000401071 <cfib.loop>:
  401071:       01 d0                   add    eax,edx
# loop entry point on first iteration, jumping over the ModRM byte (D0) of the ADD
    (entry on first iteration):
  401073:       92                      xchg   edx,eax
  401074:       e2 fb                   loop   401071 <cfib.loop>
  401076:       c3                      ret 

You can使用消耗更多后续字节的操作码来执行此操作,例如3D <dword> cmp eax, imm32。当CPU看到一个3D操作码字节,它将抓取接下来的 4 个字节作为立即数。如果您稍后跳转到这 4 个字节,它们将被视为前缀/操作码,并且无论这些字节之前如何解码为指令的不同部分,所有内容都将正常工作(性能问题除外)。除了性能之外,CPU 还必须保持一次解码和执行 1 条指令的错觉。

我从 @Ira Baxter 的回答中学到了这个技巧汇编的 ASM 代码是否可以产生多种可能的方式(偏移值除外)? https://stackoverflow.com/questions/10765317/can-assembled-asm-code-result-in-more-than-a-single-possible-way-except-for-off/10766612#10766612

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

指令解码器如何区分前缀和主操作码? 的相关文章

  • Linux内核页表更新

    在linux x86 中分页 每个进程都有它自己的页面目录 页表遍历从 CR3 指向的页目录开始 每个进程共享内核页目录内容 假设三个句子是正确的 假设某个进程进入内核 模式并更新他的内核页目录内容 地址映射 访问 权利等 问题 由于内核地
  • CPU寄存器和多任务处理

    我目前正在学习汇编 我很困惑 CPU 寄存器如何与多任务一起工作 所以在多任务系统中 CPU可以随时暂停某个程序的执行并运行另一个程序 那么在这一步中寄存器值是如何保存的呢 寄存器是压入堆栈还是以其他方式 CPU 寄存器如何与多任务一起工作
  • AVX-512 指令编码 - {er} 含义

    在 Intel x86 指令集参考中 有许多 AVX 512 指令在指令中具有可选的 er 例如 VADDPD 的一种形式定义为 EVEX NDS 512 66 0F W1 58 r VADDPD zmm1 k1 z zmm2 zmm3 m
  • linux x86 汇编语言 sys_read 调用的第一个参数应为 0 (stdin)

    我正在编写一个简单的汇编程序来从标准输入读取 如 scanf 这是我的代码 section bss num resb 5 section txt global start start mov eax 3 sys read mov ebx 0
  • FreePascal x64 上系统单元函数的汇编调用

    我有一些 Delphi 汇编代码 可以在 Win32 Win64 和 OSX 32 上编译并正常工作 XE2 但是 由于我需要它在 Linux 上工作 所以我一直在考虑编译它的 FPC 版本 到目前为止 Win32 64 Linux32 6
  • 如何编译GCC生成的asm?

    我正在玩一些汇编代码 有些事情困扰着我 我编译这个 include
  • 为什么 clang 使用 -O0 生成低效的 asm(对于这个简单的浮点和)?

    我正在 llvm clang Apple LLVM 版本 8 0 0 clang 800 0 42 1 上反汇编此代码 int main float a 0 151234 float b 0 2 float c a b printf f c
  • 如何仅使用单个数组在 JavaScript 中模拟调用堆栈

    我正在看维基百科页面 https en wikipedia org wiki Call stack在调用堆栈上 并尝试理解这个图像 据我所知 哈哈 const memory memory 0 3 top of stack pointer m
  • 是否可以在VM内使用VMX CPU指令?

    VM guest 内部的进程是否有可能使用 VMX AMD V VT x CPU 指令 然后由外部 VMM 处理而不是直接在 CPU 上处理 Edit 假设外部VM使用VMX本身来管理其虚拟客户机 即它在Ring 1中运行 如果可能的话 是
  • Visual Studio 2012 本机 C++ DLL x86 编译

    我最近将我的工具集从 Win 7 x86 Visual Studio 2010 升级到 Win 8 x64 Visual Studio 2012 但是 现在我的本机 C dll 编译为 x64 而不是 x86 除了将代码移至新操作系统并将其
  • GCC的sqrt()编译后如何工作?使用哪种root方法?牛顿-拉夫森?

    只是对标准感到好奇sqrt 来自 GCC 上的 math h 我自己编码的sqrt 使用牛顿拉夫森来做到这一点 是的 我知道 fsqrt 但CPU是如何做到这一点的呢 我无法调试硬件 现代 CPU 中的典型 div sqrt 硬件使用 2
  • LC3 LEA指令和存储的值

    我对这个问题感到困惑 指令后寄存器0中存储的值是多少 LEA R0 A 被处决了吗 为什么答案是x370C 我认为应该将A的地址加载到R0中 如果是这样我们怎么知道地址 有人可以帮忙吗 非常感谢 ORIG X3700 LEA R0 A LD
  • 在 x86 汇编中将 64 位常量移至内存

    我正在使用 Intel x64 程序集 NASM 编译器 尝试将 0x4000000000000000 常量移至内存 该常量在 ieee 754 标准双精度中应等于 2 0 我正在使用的代码是 define two 0x4000000000
  • 32位PPC rlwinm指令

    我在理解上有点困难rlwinmPPC 汇编指令 旋转左字立即然后与掩码 我正在尝试反转函数的这一部分 rlwinm r3 r3 0 28 28 我已经知道什么了r3 is r3在本例中是一个 4 字节整数 但我不确定这条指令到底是什么rlw
  • 为什么 Linux perf 使用事件 l1d.replacement 来处理 x86 上的“L1 dcache misses”?

    在英特尔 x86 上 Linux用途 https stackoverflow com a 52172985 149138事件l1d replacements来实施其L1 dcache load misses event 该事件定义如下 计数
  • 从类模板参数为 asm 生成唯一的字符串文字

    我有一个非常特殊的情况 我需要为类模板中声明的变量生成唯一的汇编程序名称 我需要该名称对于类模板的每个实例都是唯一的 并且我需要将其传递给asm关键字 see here https gcc gnu org onlinedocs gcc 12
  • 使用按位运算符相乘

    我想知道如何使用按位运算符将一系列二进制位相乘 但是 我有兴趣这样做来查找二进制值的十进制小数值 这是我正在尝试做的一个例子 假设 1010010 我想使用每个单独的位 以便将其计算为 1 2 1 0 2 2 1 2 3 0 2 4 虽然我
  • 何时可以重用avx指令中的源寄存器

    在 avx 指令中用作源的寄存器何时可以在指令开始处理后重用 例如 我想使用vgatherdps该指令消耗两个 ymm 寄存器 其中之一是位移索引 我意识到vgatherdps由于数据的局部性较差 因此需要花费大量时间来收集 位移索引寄存器
  • 整数溢出问题

    我不断遇到整数溢出问题 我不知道如何解决它 有人可以帮忙吗 edx 包含 181 eax 包含 174 xor eax edx mov edx 2 div edx 假设你谈论的是x86 div edx这实际上没有意义 32位div将edx
  • 英特尔的最后分支记录功能是英特尔处理器独有的吗?

    最后分支记录是指存储与最近执行的分支相关的源地址和目标地址的寄存器对 MSR 的集合 它们受英特尔酷睿 2 英特尔至强和英特尔凌动处理器系列的支持 http css csail mit edu 6 858 2012 readings ia3

随机推荐

  • “String”模块对象没有属性“join”

    所以 我想在 Pygame 中创建一个用户文本输入框 我被告知要查看一个名为 inputbox 的类模块 所以我下载了 inputbox py 并导入到我的主游戏文件中 然后我在其中运行一个函数并收到错误 Traceback most re
  • CSS 选择器在 :not 选择器的最后一个匹配情况后将 css 添加到下一个位置

    这是我的 HTML 代码 div class body page check in list div class body page check in item checked a div div class body page check
  • 如何将 HTML 元素传递给 React 中的高阶函数 (HOC)?

    我经常使用HOCs https reactjs org docs higher order components html为现有的 React 组件提供附加功能 这非常简单 import Component from path to Com
  • Firebase Crashlytics 不报告崩溃

    I have 成功集成 Firebase进入我的项目 身份验证和存储效果很好 但是在简单集成 Crashlytics 并故意崩溃我的应用程序之后 而不是故意 没有崩溃报告出现在崩溃仪表板中 我确实在 Logcat 中看到 D Firebas
  • Shopify CDN 的资产 URL 是如何生成的?

    假设我上传了一张名为 logo png 的图像到 Shopify 上的商店 在 Liquid 模板中 我可以为其生成一个 URL 如下所示 logo png asset url 这会生成一个 URL 例如 http static shopi
  • 使用 sql 连接查询还是使用 pandas 合并查询,哪一种更高效?

    我想在一个表中使用多个表中的数据pandas dataframe 我有两种从服务器下载数据的想法 一种方法是使用SQL连接和检索数据 一种方法是单独下载数据帧并使用 pandas merge 合并它们 SQL Join 当我想将数据下载到p
  • 脚本运行时执行时间限制

    我的 Google Apps 脚本正在迭代用户的 Google 云端硬盘文件 并将文件复制 有时将其移动到其他文件夹 该脚本总是在几分钟后停止 并且日志中没有错误消息 编者注 时间限制随着时间的推移而变化 并且 消费者 免费 和 工作空间
  • MVC .NET 上的 ajax post 未正确传递数组

    我有一个简单的模式 它使用 select2 从服务器获取产品列表 用户可以选择多个产品 然后点击 确定 来优化搜索 我的以下设置从模式中获取数据 并使用强类型视图模型对控制器操作执行 ajax 调用 该视图模型与 JS 尝试通过 ajax
  • C++反向整数序列实现

    谁知道如何实现C std make index sequence反向版本 要得到 make index sequence reverse
  • 使用嵌套类创建类的 Javascript JSON

    我想在 JavaScript 中创建一个具有嵌套对象的 JSON 对象 以下是课程 public class CellChanged private CellLocation Location null private double Cel
  • 引起原因:org.hibernate.HibernateException:找到对集合的共享引用

    我正在运行这个异常 Caused by org hibernate HibernateException Found shared references to a collection path Object listObjects 这是我
  • 如果两项服务位于同一区域,AWS 是否会产生跨账户数据传输费用?

    我想将数据从一个账户中的 EC2 机器传输到另一个账户中的 S3 存储桶 在这里 两个服务都位于同一区域 由于两者都在不同的帐户上 因此数据传输是否通过互联网进行 是的 如果 ec2 和 s3 位于不同区域 无论同一帐户或不同帐户 数据传输
  • JavaScript 数组克隆

    我有这种方法来制作数组 var playerList exports player function socket name this id socket id this name name this x 20 this y 40 retu
  • 单击时功能区按钮不会触发 onAction 设置的事件

    我设计了一个 Outlook 2010 加载项 在其中尝试触发 或者更确切地说 捕获 单击按钮时触发的事件 如图所示在本文中 http social msdn microsoft com Forums en US vsto thread a
  • 回退到 TURN 的连接的预期百分比是多少?

    假设我搭建了WebRTC视频聊天网站 握手后有些连接 ICE Candidates 会直接p2p 有些会使用STUN服务器 有些会使用 最后手段 TURN服务器来建立连接 与直接连接和 STUN 连接 免费 相比 基于 TURN 服务器的连
  • 使用 NIB 文件会导致性能损失吗?

    我很想知道是否有人有经验比较 iPhone 应用程序与 NIB 中布局的视图与完全以编程方式布局的视图 即实例化 UITextView 将其添加到视图 实例化 UIButton 将其添加到视图 的加载时间性能 如果我想要加载一个简单的应用程
  • numpy 有没有办法测试矩阵是否为酉矩阵

    我想知道 numpy 中是否有任何函数可以确定矩阵是否为酉矩阵 这是我写的函数 但它不起作用 如果你们能发现我的函数中的错误和 或告诉我另一种方法来确定给定矩阵是否是单一的 我将不胜感激 def is unitary matrix np n
  • 如何在 C++17 中读取 UTF-16 文本文件

    我对 C 很陌生 我想在 Visual Studio 2019 中读取 C 17 中的 UTF 16 文本文件 我在互联网上尝试了几种方法 包括StackOverflow 但没有一个起作用 并且其中一些无法编译 我认为它们只支持较旧的编译器
  • 如何在 numpy 或 pytorch 中向量化自定义算法?

    假设我有两个矩阵 A size k x m B size m x n 使用自定义操作 我的输出将是k x n 此自定义操作不是行之间的点积A和列B Suppose该自定义操作定义为 对于第 I 行A和第 J 列B the i j输出的元素是
  • 指令解码器如何区分前缀和主操作码?

    我正在尝试了解 x86 指令编码格式 我读过的所有资料仍然让这个主题令人困惑 我开始有点了解它 但我无法理解的一件事是 CPU 指令解码器如何区分操作码前缀和操作码 我知道指令的整个格式基本上取决于操作码 当然还有操作码中定义的额外位字段