AMD monitorx 指令的正确语法是什么?
What is the correct syntax for AMD monitorx instruction?
Ryzen 支持 monitorx
指令,如 cpuid 标志所示。不幸的是,visual studio masm 汇编程序似乎不喜欢这些指令,并且关于如何使用它们的在线文档非常少。
以下代码(非常基于AMD自己的文档)报错A2070“invalid instruction operands:
push rbx
mov eax, 5844h
mov ecx, 0
mov edx, 0
monitorx eax, ecx, edx
pop rbx
ret
我知道这段代码不是很有用,但它不应该引发构建时错误,这有什么关系?
问题是 eax、ecx 和 edx 是 32 位寄存器,但它是在 64 位模式下组装的。因为第一个操作数是指针大小,所以它必须是 64 位。以下代码适用于 64 位程序:
push rbx
mov eax, 5844h
mov ecx, 0
mov edx, 0
monitorx rax, rcx, rdx
pop rbx
ret
在机器代码中,操作数是隐式的。在汇编语法中,普通 monitorx
适用于大多数汇编程序。可以指定操作数来记录指令,或者在某些汇编程序中指定对 address-size.
的覆盖
AMD's manual 明确表示 ECX 和 EDX 是 32 位操作数。 (他们说没有定义任何提示或扩展,所以 ECX 必须为 0 否则#GP,而 EDX 被当前的 CPU 忽略。)
对于地址操作数,他们将其记录为 rAX,我认为这意味着它可以是 EAX 或 RAX。在伪代码示例中,他们使用 MONITORX EAX, ECX, EDX
,但 32 位 EAX 意味着这是一个 32-bit-mode 示例。
这是他们的第 3 卷手册,2021 年 11 月修订版 3.33,最新链接来自 https://developer.amd.com/resources/developer-guides-manuals/。
NASM 2.15.05(在 version 2.12.01 中添加了对 monitorx
的支持)
操作数列表可以指定也可以省略。如果指定,第一个操作数必须是 EAX 或 RAX,接下来的两个 必须 是 ECX 和 EDX。 (指定RCX或RDX是错误的)。
但是 NASM 不会通过使用 EAX 在 64 位模式下推断出 32 位 address-size。
可以使用 NASM 前缀指定段覆盖。
来自 nasm -l/dev/stdout -felf64 foo.asm
的示例列表:
1 00000000 0F01FA monitorx
2 00000003 0F01FA monitorx rax, ecx, edx
3 00000006 0F01FA monitorx eax, ecx, edx ; address-size override not inferred from EAX
4 00000009 670F01FA a32 monitorx ; address-size prefix, NASM style
5 0000000D 640F01FA fs monitorx rax, ecx, edx ; segment override works, too
ndisasm -b64
将其反汇编为 monitorx
、fs monitorx
或 a32 monitorx
,而不列出隐式操作数。 GNU Binutils objdump
在 -Mintel
语法模式下相同,但在 AT&T 模式下列出操作数。 (向后,以 %rax
或 %eax
作为第一个操作数,而不是像 AT&T 语法应该的那样反转!)
YASM 太旧了,很多年没更新了。我还没有尝试过 FASM。
GNU 汇编程序 (GNU Binutils) 2.36.1
与 NASM 不同,GAS 确实从 RAX 与 EAX 中推断出 address-size。 (或者在 32 位模式下,从 monitorx ax, ecx, edx
推断 16 位地址大小)
GAS 不 检查最后两个操作数的大小,只是它们是 ECX、EDX 的某个版本(dl/dh 除外)。例如,monitorx rax, rcx, dx
毫无怨言地组装。对最后一个操作数使用 ebx
或 dl
给出了“错误:'monitorx' 的操作数类型不匹配”,这毫无用处,因为问题不在于类型,而在于寄存器。 (这是典型的 GAS 错误消息,对人类的帮助通常不如 NASM。)
英特尔语法模式,.intel_syntax noprefix
objdump -d -Mintel disassembly | source
| .intel_syntax noprefix
0f 01 fa monitorx | monitorx
0f 01 fa monitorx | monitorx rax, ecx, edx
67 0f 01 fa addr32 monitorx | monitorx eax, ecx, edx # address-size inferred from EAX
67 0f 01 fa addr32 monitorx | addr32 monitorx # address-size prefix
64 0f 01 fa fs monitorx | fs monitorx rax, ecx, edx # segment override works, too
AT&T 语法模式
GAS 令人惊讶地使用与 Intel 语法相同的 operand-order,而不是像您对 AT&T 语法所期望的那样颠倒过来。所以 %rax
/ %eax
仍然是第一个。
GNU Binutils objdump
在反汇编为 AT&T 语法时选择包含操作数,这与 -Mintel
不同。
objdump -d -Matt disassembly (trimmed) | source
-------------------------------------------+---------------------------
0f 01 fa monitorx %rax,%ecx,%edx | monitorx
0f 01 fa monitorx %rax,%ecx,%edx | monitorx %rax, %ecx, %edx
67 0f 01 fa monitorx %eax,%ecx,%edx | monitorx %eax, %ecx, %edx # address-size inferred from EAX
67 0f 01 fa monitorx %eax,%ecx,%edx | addr32 monitorx # address-size prefix
64 0f 01 fa fs monitorx %rax,%ecx,%edx | fs monitorx %rax, %ecx, %edx # segment override works, too
clang 13.0.0 与 LLVM 的 built-in 汇编器 / llvm-objdump
llvm-objdump
给出无用的输出,不表示存在 address-size 或段覆盖:
0: 0f 01 fa monitorx
3: 0f 01 fa monitorx
6: 67 0f 01 fa monitorx
a: 67 0f 01 fa monitorx
e: 64 0f 01 fa monitorx
同样,LLVM 的 built-in 汇编程序似乎根本不接受操作数,只是说“指令操作数无效”。
| .intel_syntax noprefix
0f 01 fa | monitorx
| #monitorx rax, ecx, edx # error
| #monitorx eax, ecx, edx # error
67 0f 01 fa | addr32 monitorx # address-size prefix works
64 0f 01 fa | fs monitorx # segment override works
在 AT&T 语法模式下同样处理,无论是否反转 operand-list。它不接受 monitorx %edx, %ecx, %rax
或 monitorx %rax, %ecx, %edx
.
无论哪种方式,LLVM 都接受 monitorx
并正确组装前缀。
只有 LLVM-objdump 反汇编程序无法以任何方式显示前缀。
Ryzen 支持 monitorx
指令,如 cpuid 标志所示。不幸的是,visual studio masm 汇编程序似乎不喜欢这些指令,并且关于如何使用它们的在线文档非常少。
以下代码(非常基于AMD自己的文档)报错A2070“invalid instruction operands:
push rbx
mov eax, 5844h
mov ecx, 0
mov edx, 0
monitorx eax, ecx, edx
pop rbx
ret
我知道这段代码不是很有用,但它不应该引发构建时错误,这有什么关系?
问题是 eax、ecx 和 edx 是 32 位寄存器,但它是在 64 位模式下组装的。因为第一个操作数是指针大小,所以它必须是 64 位。以下代码适用于 64 位程序:
push rbx
mov eax, 5844h
mov ecx, 0
mov edx, 0
monitorx rax, rcx, rdx
pop rbx
ret
在机器代码中,操作数是隐式的。在汇编语法中,普通 monitorx
适用于大多数汇编程序。可以指定操作数来记录指令,或者在某些汇编程序中指定对 address-size.
AMD's manual 明确表示 ECX 和 EDX 是 32 位操作数。 (他们说没有定义任何提示或扩展,所以 ECX 必须为 0 否则#GP,而 EDX 被当前的 CPU 忽略。)
对于地址操作数,他们将其记录为 rAX,我认为这意味着它可以是 EAX 或 RAX。在伪代码示例中,他们使用 MONITORX EAX, ECX, EDX
,但 32 位 EAX 意味着这是一个 32-bit-mode 示例。
这是他们的第 3 卷手册,2021 年 11 月修订版 3.33,最新链接来自 https://developer.amd.com/resources/developer-guides-manuals/。
NASM 2.15.05(在 version 2.12.01 中添加了对 monitorx
的支持)
操作数列表可以指定也可以省略。如果指定,第一个操作数必须是 EAX 或 RAX,接下来的两个 必须 是 ECX 和 EDX。 (指定RCX或RDX是错误的)。
但是 NASM 不会通过使用 EAX 在 64 位模式下推断出 32 位 address-size。
可以使用 NASM 前缀指定段覆盖。
来自 nasm -l/dev/stdout -felf64 foo.asm
的示例列表:
1 00000000 0F01FA monitorx
2 00000003 0F01FA monitorx rax, ecx, edx
3 00000006 0F01FA monitorx eax, ecx, edx ; address-size override not inferred from EAX
4 00000009 670F01FA a32 monitorx ; address-size prefix, NASM style
5 0000000D 640F01FA fs monitorx rax, ecx, edx ; segment override works, too
ndisasm -b64
将其反汇编为 monitorx
、fs monitorx
或 a32 monitorx
,而不列出隐式操作数。 GNU Binutils objdump
在 -Mintel
语法模式下相同,但在 AT&T 模式下列出操作数。 (向后,以 %rax
或 %eax
作为第一个操作数,而不是像 AT&T 语法应该的那样反转!)
YASM 太旧了,很多年没更新了。我还没有尝试过 FASM。
GNU 汇编程序 (GNU Binutils) 2.36.1
与 NASM 不同,GAS 确实从 RAX 与 EAX 中推断出 address-size。 (或者在 32 位模式下,从 monitorx ax, ecx, edx
推断 16 位地址大小)
GAS 不 检查最后两个操作数的大小,只是它们是 ECX、EDX 的某个版本(dl/dh 除外)。例如,monitorx rax, rcx, dx
毫无怨言地组装。对最后一个操作数使用 ebx
或 dl
给出了“错误:'monitorx' 的操作数类型不匹配”,这毫无用处,因为问题不在于类型,而在于寄存器。 (这是典型的 GAS 错误消息,对人类的帮助通常不如 NASM。)
英特尔语法模式,.intel_syntax noprefix
objdump -d -Mintel disassembly | source
| .intel_syntax noprefix
0f 01 fa monitorx | monitorx
0f 01 fa monitorx | monitorx rax, ecx, edx
67 0f 01 fa addr32 monitorx | monitorx eax, ecx, edx # address-size inferred from EAX
67 0f 01 fa addr32 monitorx | addr32 monitorx # address-size prefix
64 0f 01 fa fs monitorx | fs monitorx rax, ecx, edx # segment override works, too
AT&T 语法模式
GAS 令人惊讶地使用与 Intel 语法相同的 operand-order,而不是像您对 AT&T 语法所期望的那样颠倒过来。所以 %rax
/ %eax
仍然是第一个。
GNU Binutils objdump
在反汇编为 AT&T 语法时选择包含操作数,这与 -Mintel
不同。
objdump -d -Matt disassembly (trimmed) | source
-------------------------------------------+---------------------------
0f 01 fa monitorx %rax,%ecx,%edx | monitorx
0f 01 fa monitorx %rax,%ecx,%edx | monitorx %rax, %ecx, %edx
67 0f 01 fa monitorx %eax,%ecx,%edx | monitorx %eax, %ecx, %edx # address-size inferred from EAX
67 0f 01 fa monitorx %eax,%ecx,%edx | addr32 monitorx # address-size prefix
64 0f 01 fa fs monitorx %rax,%ecx,%edx | fs monitorx %rax, %ecx, %edx # segment override works, too
clang 13.0.0 与 LLVM 的 built-in 汇编器 / llvm-objdump
llvm-objdump
给出无用的输出,不表示存在 address-size 或段覆盖:
0: 0f 01 fa monitorx
3: 0f 01 fa monitorx
6: 67 0f 01 fa monitorx
a: 67 0f 01 fa monitorx
e: 64 0f 01 fa monitorx
同样,LLVM 的 built-in 汇编程序似乎根本不接受操作数,只是说“指令操作数无效”。
| .intel_syntax noprefix
0f 01 fa | monitorx
| #monitorx rax, ecx, edx # error
| #monitorx eax, ecx, edx # error
67 0f 01 fa | addr32 monitorx # address-size prefix works
64 0f 01 fa | fs monitorx # segment override works
在 AT&T 语法模式下同样处理,无论是否反转 operand-list。它不接受 monitorx %edx, %ecx, %rax
或 monitorx %rax, %ecx, %edx
.
无论哪种方式,LLVM 都接受 monitorx
并正确组装前缀。
只有 LLVM-objdump 反汇编程序无法以任何方式显示前缀。