gcc - 您如何解释 gcc 对 i386 的 IN、OUT 指令的内联汇编约束?
问题描述
据我所知,gcc 内联汇编中使用的约束告诉 gcc 输入和输出变量必须去哪里(或必须去哪里)才能生成有效的汇编。正如《精细手册》所说,“对操作数位置的限制”。
这是教程中的一个具体的工作示例。
static inline uint8_t inb(uint16_t port)
{
uint8_t ret;
asm volatile ( "inb %1, %0"
: "=a"(ret)
: "Nd"(port) );
return ret;
}
inb
IN
是从 I/O 端口接收一个字节的 i386 指令的 AT&T 语法。
以下是该指令的规格,取自 i386 手册。请注意,端口号从0x0000
到0xFFFF
。
IN AL,imm8 // Input byte from immediate port into AL
IN AX,imm8 // Input word from immediate port into AX
IN EAX,imm8 // Input dword from immediate port into EAX
IN AL,DX // Input byte from port DX into AL
IN AX,DX // Input word from port DX into AX
IN EAX,DX // Input dword from port DX into EAX
uint8_t x = inb(0x80);
给定一个像汇编输出这样的语句,正确地是inb $0x80,%al
. 它使用IN AL,imm8
了指令的形式。
现在,假设我只关心IN AL,imm8
表单,从介于两者uint8_t
之间的端口接收 a 。这个和工作示例之间的唯一区别是现在是模板参数(使其有效地成为常量)并且约束是 now 。0x00
0xFF
port
uint8_t
"N"
template<uint8_t port>
static inline uint8_t inb()
{
uint8_t ret;
asm volatile ( "inb %1, %0"
: "=a"(ret)
: "N"(port) );
return ret;
}
失败!
我认为“N”约束意味着“你必须有一个恒定的无符号 8 位整数用于这条指令”,但显然它不是,因为它是一个“不可能的约束”。模板参数不是uint8_t
常量无符号 8 位整数吗?
如果我用“Nd”替换“N”,我会得到一个不同的错误:
./test.h: Assembler messages:
./test.h:23: Error: operand type mismatch for `in'
在这种情况下,汇编程序的输出inb %dl, %al
显然是无效的。
为什么这只适用于"Nd"
anduint16_t
而不是"N"
and uint8_t
?
编辑:
这是我在 godbolt.org 上尝试过的精简版:
#include <cstdint>
template<uint8_t N>
class Port {
public:
uint8_t in() const {
uint8_t data;
asm volatile("inb %[port], %%al"
:
: [port] "N" (N)
: // clobbers
);
return data;
}
};
void func() {
Port<0x7F>().in();
}
有趣的是,这很好用,除非您将 N 更改为 0x80 和 0xFF 之间的任何值。在铿锵声中,这会产生“128 超出约束 N 的范围”错误。这会在 gcc 中产生更一般的错误。
解决方案
根据约束的记录方式,您的代码应该按预期工作。
一年多后,这似乎仍然是一个错误。看来编译器正在从无N
符号值转换为有符号值,并试图将其传递给内联汇编约束。当传递给约束的值不能表示为 8 位有符号值时,这当然会失败。假设输入约束"N"
允许一个无符号的8 位值,并且应该接受 0 到 255 (0xff) 之间的任何值:
ñ
无符号 8 位整数常量(用于输入和输出指令)。
GCC 的 bugzilla有一个类似的错误报告,标题为“常量约束检查符号扩展无符号常量输入操作数”。
在其中一个相关线程中,建议您可以通过 ANDing (&) 0xff 到常量(即:)来解决此问题N & 0xff
。我还发现静态转换N
为更广泛的无符号类型uint8_t
也有效:
#include <cstdint>
template<uint8_t N>
class Port {
public:
uint8_t in() const {
uint8_t data;
asm volatile("inb %[port], %0"
: "=a"(data)
: [port] "N" (static_cast<uint16_t>(N))
: // clobbers
);
return data;
}
};
void func() {
Port<0x7f>().in();
Port<0x80>().in();
// Port<0x100>().in(); // Fails as expected since it doesn't fit in a uint8_t
}
要对此进行测试,您可以在Godbolt上使用它。
推荐阅读
- reactjs - 用 mobx 测试 react native 的观察者组件
- python - 如何使用 BigBirdModel 在 Python 中创建神经网络?
- javascript - 过滤包含零值的对象
- sql-server - 执行存储过程的 Powershell 函数返回 -1 并且不加载表中的数据
- python - 更改 Pandas 数据框布局
- r - 使用其类从网页中提取链接列表
- java - 给定forkcount = 1.0C,maven surefire插件选项“resuseFork = true”是否比“reuseFork = false”快?
- c# - 如何使用 EF Core 更新具有并发控制的行?
- python - 在 OOP 中使用 dict 表示信用评分的最有效方法
- mongodb - mongodb $pull 在数组中与另一个数组