AMD Monitorx 指令的正确语法是什么?
Ryzen 支持 monitorx
指令,如 cpuid 标志所示。不幸的是,Visual Studio masm 汇编器似乎不喜欢这些说明,并且网上关于如何使用它们的文档很少。
以下代码(非常基于 AMD 自己的文档)报告错误 A2070“无效指令操作数:
push rbx
mov eax, 5844h
mov ecx, 0
mov edx, 0
monitorx eax, ecx, edx
pop rbx
ret
我知道这段代码不是很有用,但它不应该抛出构建时错误,所以这是怎么回事?
Ryzen supports the monitorx
instructions, as indicated by the cpuid flag. Unfortunately the visual studio masm assembler doesn't seem to like these instructions, and there is very little documentation online for how to use them.
The following code (which is very based on AMD's own documentation) reports the error A2070 "invalid instruction operands:
push rbx
mov eax, 5844h
mov ecx, 0
mov edx, 0
monitorx eax, ecx, edx
pop rbx
ret
I understand that this code isn't very useful, but it shouldn't be throwing build time errors, so what's the deal?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
问题是 eax、ecx 和 edx 是 32 位寄存器,但它是以 64 位模式组装的。因为第一个操作数是指针大小,所以它必须是 64 位。以下代码适用于 64 位程序:
The problem is that eax, ecx, and edx are 32 bit registers, but it was being assembled in 64 bit mode. Because the first operand is pointer size, it must be 64 bits. The following code will work on 64 bit programs:
在机器代码中,操作数是隐式的。在汇编语法中,普通的
monitorx
适用于大多数汇编器。可以指定操作数来记录指令,或者在某些汇编器中指定对地址大小的覆盖。AMD 手册明确指出 ECX 和 EDX 是 32 位操作数。 (他们说没有定义任何提示或扩展,因此 ECX 必须为 0,否则 #GP,并且 EDX 会被当前 CPU 忽略。)
对于地址操作数,他们将其记录为 rAX,我认为这意味着它可以是 EAX 或 RAX 。在伪代码示例中,他们使用 MONITORX EAX、ECX、EDX,但 32 位 EAX 意味着这是一个 32 位模式示例。
这是他们的第 3 卷手册,2021 年 11 月修订版 3.33,最新链接来自 https://developer.amd.com/resources/developer-guides-manuals/。
NASM 2.15.05(在 版本 2.12 中添加了对
monitorx
的支持。 01)操作数列表可以指定或省略。如果指定,第一个操作数必须是 EAX 或 RAX,接下来的两个操作数必须是 ECX 和 EDX。 (指定 RCX 或 RDX 是错误的)。
但 NASM 不会通过使用 EAX 推断 64 位模式下的 32 位地址大小。
可以使用 NASM 前缀指定段覆盖。
来自
nasm -l/dev/stdout -felf64 foo.asm
的示例清单:ndisasm -b64
将其反汇编为monitorx
、fs Monitorx
或a32 monitorx
而不列出隐式操作数。 GNU Binutilsobjdump
与-Mintel
语法模式相同,但在 AT&T 模式下列出操作数。 (向后,以%rax
或%eax
作为第一个操作数,而不是像 AT&T 语法那样反转!)YASM 太旧了,尚未更新年。我没试过FASM。
GNU 汇编器 (GNU Binutils) 2.36.1
与 NASM 不同,GAS 确实根据 RAX 与 EAX 推断地址大小。 (或者在 32 位模式下,从
monitorx ax, ecx, edx
推断 16 位地址大小)GAS 不会检查最后两个操作数的大小,只是检查它们是 ECX、EDX 的某些版本(dl/dh 除外)。例如,
monitorx rax, rcx, dx
可以毫无怨言地进行汇编。对最后一个操作数使用 ebx 或 dl 会出现“错误:‘monitorx’的操作数类型不匹配”,这是相当无用的,因为问题不是类型,而是问题所在注册它是。 (这是典型的 GAS 错误消息,通常对人类的帮助不如 NASM。)Intel 语法模式,
.intel_syntax noprefix
AT&T 语法模式
GAS 令人惊讶地使用与 Intel 语法相同的操作数顺序,而不是就像您对 AT&T 语法所期望的那样相反。所以
%rax
/%eax
仍然是第一位。GNU Binutils
objdump
选择在反汇编为 AT&T 语法时包含操作数,这与-Mintel
不同。clang 13.0.0 与 LLVM 的内置汇编器 /
llvm-objdump
llvm-objdump
给出无用的输出,不指示地址大小或段覆盖的存在:同样,LLVM 的内置汇编器似乎根本不接受操作数,只是说“指令操作数无效”。
无论您是否反转操作数列表,AT&T 语法模式中的处理都是相同的。它不接受
monitorx %edx、%ecx、%rax
或monitorx %rax、%ecx、%edx
。无论哪种方式,LLVM 都会接受
monitorx
并正确组装前缀。只有 LLVM-objdump 反汇编程序无法以任何方式显示前缀。
In machine code, the operands are implicit. In assembly syntax, plain
monitorx
works for most assemblers. The operands can be specified to document the instruction, or in some assemblers to specify an override to the address-size.AMD's manual explicitly says that ECX and EDX are 32-bit operands. (They say there are no hints or extensions defined, so ECX must be 0 else #GP, and EDX is ignored by current CPUs.)
For the address operand, they document it as rAX, which I think means it can be EAX or RAX. In the pseudocode example, they use
MONITORX EAX, ECX, EDX
, but the 32-bit EAX means that's a 32-bit-mode example.This is in their vol.3 manual, Nov. 2021 rev 3.33, the latest linked from https://developer.amd.com/resources/developer-guides-manuals/.
NASM 2.15.05 (support added for
monitorx
in version 2.12.01)The operand list can be specified or omitted. If specified, the first operand must be EAX or RAX, and the next two must be ECX and EDX. (It's an error to specify RCX or RDX).
But NASM doesn't infer 32-bit address-size in 64-bit mode from using EAX.
Segment overrides can be specified with NASM prefixes.
Example listing from
nasm -l/dev/stdout -felf64 foo.asm
:ndisasm -b64
disassembles it as justmonitorx
,fs monitorx
, ora32 monitorx
without listing the implicit operands. GNU Binutilsobjdump
is the same in-Mintel
syntax mode, but lists operands in AT&T mode. (Backwards, with%rax
or%eax
as the first operand, not reversed like AT&T syntax should be!)YASM is too old, hasn't been updated in years. I haven't tried FASM.
GNU assembler (GNU Binutils) 2.36.1
Unlike NASM, GAS does infer the address-size from RAX vs. EAX. (Or in 32-bit mode, infer 16-bit address size from
monitorx ax, ecx, edx
)GAS does not check sizes on the last two operands, only that they're some version of ECX, EDX (other than dl/dh). For example,
monitorx rax, rcx, dx
assembles without complaint. Usingebx
ordl
for the last operand gave "Error: operand type mismatch for 'monitorx'", which is rather unhelpful because it's not the type that's the problem, it's which register it is. (This is typical of GAS error messages, often less helpful to humans than NASM.)Intel syntax mode,
.intel_syntax noprefix
AT&T syntax mode
GAS surprisingly uses the same operand-order as Intel syntax, not reversed like you'd expect for AT&T syntax. So
%rax
/%eax
is still first.GNU Binutils
objdump
chooses to include operands when disassembling into AT&T syntax, unlike with-Mintel
.clang 13.0.0 with LLVM's built-in assembler /
llvm-objdump
llvm-objdump
gives unhelpful output, not indicating the presence of address-size or segment overrides:Similarly, LLVM's built-in assembler doesn't seem to accept operands at all, just saying "invalid operand for instruction".
Same deal in AT&T syntax mode, whether you reverse the operand-list or not. It doesn't accept
monitorx %edx, %ecx, %rax
ormonitorx %rax, %ecx, %edx
.Either way, LLVM does accept
monitorx
and correctly assembles prefixes.It's only the LLVM-objdump disassembler that fails to show prefixes in any way.