未定义的行为是否适用于 asm 代码

Does undefined behavior apply to asm code?

本文关键字:适用于 asm 代码 是否 未定义      更新时间:2023-10-16

假设你知道你的软件只能在两个补码机器上运行,其中签名溢出行为得到了很好的定义。在 C 和 C++ 中,签名溢出仍然是未定义的行为,编译器可以自由地用"ret"替换整个程序、发动核战争、格式化驱动器或让恶魔从您的鼻子里飞出。

假设您已在内联 asm 中对溢出进行了签名,您的程序是否仍调用 UB?

如果是,那么单独编译和链接的汇编程序呢?

"

未定义的行为"意味着C和C++标准没有定义程序的行为。如果你的程序包含内联汇编,那么应该很清楚,它的行为通常不会由 C 或 C++ 标准描述。其他一些标准甚至可能定义行为,但这仍然不意味着在C或C++标准的上下文中"定义的行为"。

也就是说,C 标准确实需要支持扩展的文档。如果程序的行为可以从实现的文档推断出来,并且实现使程序的行为不同,则说明实现不符合标准:

4. 一致性

8 实现应附有一份文档,其中定义了所有实现定义和特定于区域设置的特征以及所有扩展。

对于C++,此要求已被削弱:

1.4 实现合规性

9 每个实现都应包括文档,这些文档标识了它不支持的所有有条件支持的构造,并定义了所有特定于区域设置的特征。

1.9 程序执行

2 抽象机器的某些方面和操作在本国际标准中描述为实现定义的 [...]每个实现都应包括描述其在这些方面的特征和行为的文档。[...]

我找不到要记录扩展的要求,

如果记录了,则正确记录。这表明,在C++中,即使您的实现将程序的行为定义为扩展,如果事实证明文档是错误的,那也太糟糕了。

对于C++半标准asm语句(如注释中所述,"asm声明是有条件支持的;其含义是实现定义的。 如果你的实现支持它,则需要记录它,但当然,实现通常的做法是以与C++标准所暗示的不同方式支持内联汇编, 所以这不会给你太多额外的东西。

一旦你说你在内联 asm 中签名溢出,这意味着你说的是一个特定的编译器(或一组编译器),因为在 C 和C++中一样,对 asm 声明及其含义的支持是编译器定义的

如果编译器通过允许在其输出中直接包含汇编代码来定义 asm 关键字,并且如果机器允许有符号溢出,那么内联 asm 中的签名溢出是为该编译器和该机器完美定义的:这是处理器将给出的结果。您仍应控制它是否可以导致有符号整数的陷阱表示形式,但无论如何它都是定义的。唯一以 UB 结尾的情况是编译器说有符号整数中的某些表示会导致未定义的行为。但我知道没有这样做,你已经在一组定义和有限的编译器和机器的上下文中。

程序集模块和 C 和/或C++代码的单独编译对于这组编译器和机器是相同的:结果是定义的实现与 UB 不同。

标准(C 和 C++)中明确定义的实现的另一个示例是char类型是否是有符号的:如果你不知道你使用什么编译器,你就不能依赖它,但是一旦你选择了编译器实现,该实现就需要说明它是有符号的还是无符号的, 它不是未定义的行为,这意味着编译器不能用 RET 替换完整的代码。