给定堆栈和寄存器的状态,我们可以预测 printf 未定义行为的结果吗

Given the state of the stack and registers, can we predict the outcome of printf's undefined behavior

这里有一些简单的 C 代码用于 class 测验:

#include <stdio.h>

int main() {
  float a = 2.3;
  printf("%d\n", a);
  return 0;
}

已编译并 运行 于:

Apple LLVM 版本 6.1.0 (clang-602.0.53)(基于 LLVM 3.6.0svn)
目标:x86_64-apple-darwin14.5.0

这段代码的输出是undefined。我试图通过使用调试器(gdb 中的 X 命令)检查 a 附近的内存来预测输出。比如a的地址是0x7fff5fbffb98,那么&a附近的上下文如下:

0x7fff5fbffb98: 1075000115
0x7fff5fbffb9c: 0
0x7fff5fbffba0: 1606417336
0x7fff5fbffba4: 32767
0x7fff5fbffba8: -1754266167
0x7fff5fbffbac: 32767
0x7fff5fbffbb0: -1754266167
0x7fff5fbffbb4: 32767

那么printf的输出就是1606417352。我知道使用不正确的说明符时的输出是未定义的。出于好奇,我预计此未定义行为的输出与 运行ning 堆栈或寄存器中的某些内存相关,但我还没有弄清楚如何关联它。

那么这个printf的输出是用哪个地址或者寄存器来设置的呢?换句话说,给定 运行ning 堆栈的状态,以及来自所有寄存器的所有值,我们能否预测(如果是的话如何预测)这种未定义行为的输出?

在带有 SysV calling convention 的 AMD64 上(几乎每个系统都使用,但 Windows),函数的前几个参数在寄存器中传递 这就是为什么你在堆栈上看不到它们的原因:它们没有在堆栈上传递。

具体来说,前几个整数或指针参数在rdirsirdx中传递,而前几个浮点参数在xmm0中传递、xmm1xmm2。由于 a 是在 xmm0 中传递的,但是 printf 试图从 rsi 中读取一个数字,您将看不到您提供的数字与打印出来的数字之间的任何关联。


对于未来的读者:请注意,OP 试图做的是 undefined behavior。 ISO 9899:2011 指定应为 %d 传递 int,但 OP 试图将其与 double 一起使用(在默认参数提升之后)。为此,OP 应该使用 %f 代替。使用错误的格式说明符是未定义的行为。请不要假设 OP 的观察结果适用于您的系统或任何地方,也不要编写此类代码。

您尝试使用 %d 作为 float:

d 说明符用于 有符号十进制整数

f说明符用于十进制浮点数

使用错误的说明符导致 Undefined behavior

你依赖了一个自动变量的地址:

I try to predict the output by viewing the memory near a

a是一个自动变量,每次编译它的地址都会改变,所以memory-near-a也会随着每次编译而改变。

因此,"viewing the memory near a" 也会导致未定义的行为。

解法:

你与未定义的行为无关(在这种情况下),所以为了节省时间而忘记它,它会让你的生活更轻松。