[英]what does the colon in asm volatile() mean
我不确定我是否不小心修改了代码,但这里是(我是内联汇编的初学者,但非常习惯于在不同的文件中进行汇编):-
void out8(uint16 port, uint8 data) {asm volatile("outb %0, %1" : "dN"(port) : "a"(data));}
void out16(uint16 port, uint16 data) {asm volatile("outw %0, %1" : "dN"(port) : "a"(data));}
void out32(uint16 port, uint32 data) {asm volatile("outl %%eax, %%dx" : "dN"(port) : "a"(data));}
以前它没有出错,但现在是。 但是任何人都可以更正此代码吗? 而且,告诉我冒号分隔值的依据是什么,冒号分隔区域中的“dN”和“a”,内联汇编中的“%0”和“%1”,为什么这些变量“ “a”和“dN”旁边括号中的“端口”和“数据”以及“% [reg] ”和“%% [reg] ”之间的区别是什么,以便我以后可以解决此类问题他们。 (tl;du(你代表理解)内联扩展汇编的手册页是日语(你知道我的意思,对吧?))
[解决了]
用过的 :-
void out(uint16 port, uint8 data) {asm volatile("outb %1, %0" :: "dN"(port), "a"(data));}
void out(uint16 port, uint16 data) {asm volatile("outw %1, %0" : : "dN"(port), "a"(data));}
// Warning, this one's still unsafe, even though it compiles
void out(uint16 port, uint32 data) {asm volatile("outl %%eax, %%dx" : : "dN"(port), "a"(data));}
(警告未来的读者: outl
仍然存在错误,请参阅答案。)
显然,阅读语法手册。 https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html 。
asm asm-qualifiers ( AssemblerTemplate
: OutputOperands
[ : InputOperands
[ : Clobbers ] ])
另请参阅https://stackoverflow.com/questions/tagged/inline-assembly以获取指向官方文档以外的指南的链接。
您在输出部分有"dN"(port)
,但它实际上是一个输入(没有=
或+
)。 是的,I/O 端口号应该是一个输入:它是 asm 语句需要从编译器获取的东西,而不是 asm 语句提供回编译器的东西。
如果你对out
有两个“输入”这一事实感到困惑,可以把它想象成一条存储指令。 两条数据来自 CPU(数据和地址),导致存储到内存。 不同于负载或in
其中负载指令写入的寄存器,并且编译器需要知道该结果被放置。
假设这是针对 AT&T 语法(不是gcc -masm=intel
) %0, %1
您还有%0, %1
操作数的顺序错误。 命名约束如[port] "dN" (port)
以匹配模板中的%[port]
将避免这种情况。 https://www.felixcloutier.com/x86/out在 Intel 语法中是out dx/imm8, AL/AX/EAX
,或者在 AT&T 中out %al/%ax/%eax, %dx/$imm8
。
out32()
。 "dN"
约束允许编译器为该变量选择 DX 或立即数(如果其值在编译时已知并且足够小)。
但是你的 asm 模板字符串没有引用%0
第一个操作数,而是硬编码%%dx
寄存器名称,这只有在编译器选择 DX 时才是正确的。
内联out32(0x80, 0x1234)
的优化构建不会使用前面的指令将端口号放入 DX 中,而是选择N
(无符号 8 位)约束,因为它更便宜。 但是在最终编译器生成的 asm 中没有任何地方填充$0x80
,因为模板中没有%0
供编译器扩展。
在将整个 asm 传递给汇编程序之前,将 asm 模板字符串考虑为编译器扩展的 printf 格式字符串。 (包括编译器在编译其他 C 语句之前和之后生成的指令,以及一些约束,如"r"
或"d"
以将 C 变量或表达式的值放入寄存器(如果尚不存在)。
%%
只是一个文字%
,所以如果你想硬编码一个 AT&T 寄存器名称,比如%eax
,你可以在扩展 Asm 模板中使用%%eax
。
你可以在https://godbolt.org/上看到那个 asm。 (使用“二进制”模式查看生成的编译器生成的 asm 是否会实际组装。使用内联 asm 时不能保证这一点。)
对于outb
/ etc. 宏,许多代码库定义了它们,我认为一些 libc 实现具有内联包装器,例如可能是 MUSL,也可能是 glibc。 如果你只是想要工作代码,当你不知道内联 asm 时不要尝试编写自己的代码。
有关的:
你如何解释gcc对i386的IN、OUT指令的内联汇编约束? 分解inb
包装函数。
用于 x86 输入/输出端口 I/O 的 C inline asm 的操作数大小不匹配,为"d"
选择的寄存器大小取决于 C 类型宽度,并且必须为 16 位,因为in
/ out
专门使用 DX。 (I/O 地址空间是 64k,与内存地址空间不同。)
https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html手册。 阅读它。 内联汇编很容易得到微妙但危险的错误; 不要依赖反复试验/“似乎有效”。
https://gcc.gnu.org/wiki/DontUseInlineAsm 。 (或者在这种情况下,如果您不了解内联汇编,请在 libc 头文件或其他内容中找到一些已知良好的输入/输出包装函数,并使用它们而不是编写自己的函数。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.