给定堆栈和寄存器的状态,我们可以预测 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),函数的前几个参数在寄存器中传递 。这就是为什么你在堆栈上看不到它们的原因:它们没有在堆栈上传递。
具体来说,前几个整数或指针参数在rdi
、rsi
、rdx
中传递,而前几个浮点参数在xmm0
中传递、xmm1
和 xmm2
。由于 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" 也会导致未定义的行为。
解法:
你与未定义的行为无关(在这种情况下),所以为了节省时间而忘记它,它会让你的生活更轻松。
这里有一些简单的 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),函数的前几个参数在寄存器中传递 。这就是为什么你在堆栈上看不到它们的原因:它们没有在堆栈上传递。
具体来说,前几个整数或指针参数在rdi
、rsi
、rdx
中传递,而前几个浮点参数在xmm0
中传递、xmm1
和 xmm2
。由于 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" 也会导致未定义的行为。
解法:
你与未定义的行为无关(在这种情况下),所以为了节省时间而忘记它,它会让你的生活更轻松。