【问题标题】:Does undefined behavior apply to asm code?未定义的行为是否适用于 asm 代码?
【发布时间】:2016-03-01 18:31:02
【问题描述】:

假设您知道您的软件只能在二进制补码机器上运行,其中有符号溢出行为得到了很好的定义。有符号溢出在 C 和 C++ 中仍然是未定义的行为,编译器可以随意用“ret”替换整个程序、发动核战争、格式化驱动器或让恶魔飞出你的鼻子。

假设你在内联 asm 中签名溢出,你的程序是否仍然调用 UB?

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

【问题讨论】:

  • 这是一个非常有趣的问题。我也想知道。
  • 首先内联汇编甚至是标准 C 或 C++ 吗?老实说,我不知道。如果不是,我觉得整个问题都站不住脚。
  • 嗯,有 [dcl.asm]:asm 声明是有条件支持的;它的含义是实现定义的。
  • 对于 C99,我发现的唯一内容是 asm 关键字:J.5.10 asm 关键字可用于将汇编语言直接插入翻译器输出 (6.8)。
  • 很多未定义的行为实际上是由实现定义的。 “未定义的行为”不是意味着“实现必须使行为尽可能随机和邪恶”。

标签: c++ c assembly language-lawyer undefined-behavior


【解决方案1】:

“未定义的行为”是指 C 语言。 C++ 标准不定义程序的行为。如果您的程序包含内联汇编,那么应该很清楚它的行为通常不会被 C 或 C++ 标准描述。其他一些标准甚至可以定义行为,但这并不意味着在 C 或 C++ 标准的上下文中“定义的行为”。

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

4.一致性

8 实施应附有一份文件,该文件定义所有实施定义的和特定于语言环境的特征和所有扩展。

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

1.4 实施合规性 [intro.compliance]

9 每个实现都应包含文档,以标识它不支持的所有有条件支持的构造,并定义所有特定于语言环境的特征。

1.9 程序执行 [intro.execution]

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

我找不到对扩展进行记录的要求,如果记录在案,则需要正确记录。这表明在 C++ 中,即使您的实现将程序的行为定义为扩展,但如果证明文档是错误的,那就太糟糕了。

对于 C++ 半标准 asm 语句(如 cmets 中所述,“asm 声明是有条件支持的;其含义是实现定义的。”),如果您的实现支持它,则需要记录在案,但当然,实现以不同于 C++ 标准暗示的方式支持内联汇编的常见做法,因此这不会给您带来太多额外的好处。

【讨论】:

  • 好的,所以根据标准,它的实现定义了内联汇编对程序的意义。这就引出了一个问题,流行的编译器实际上是做什么的?
  • @Eloff 这与你在这个问题中提出的问题非常不同,在这里回答太多了。
【解决方案2】:

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

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

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

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

【讨论】:

    猜你喜欢
    • 2015-02-16
    • 2018-01-29
    • 2011-07-25
    • 1970-01-01
    • 2021-02-14
    • 2015-05-26
    • 1970-01-01
    • 2016-09-15
    • 1970-01-01
    相关资源
    最近更新 更多