__printf_chk
是一个包装器printf
它检查堆栈溢出,并采用一个额外的第一个参数,一个标志(例如,参见here http://refspecs.freestandards.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/libc---printf-chk-1.html.)
pow(2, 64) - 1
已优化为0xffffffffffffffff
因为参数是常量。
根据通常的调用约定,第一个参数__printf_chk()
(int flag
)是堆栈上的 32 位值(位于%esp
在当时的call
操作说明)。下一个论点,const char * format
,是一个 32 位指针(堆栈上的下一个 32 位字,即位于%esp+4
)。正在打印的 64 位数量占据接下来的两个 32 位字(在%esp+8
and %esp+12
):
pushl %ebp ; prologue
movl %esp, %ebp ; prologue
andl $-16, %esp ; align stack pointer
subl $16, %esp ; reserve bytes for stack frame
movl $-1, 8(%esp) #1 ; store low half of 64-bit argument (a constant) to stack
movl $-1, 12(%esp) #2 ; store high half of 64-bit argument (a constant) to stack
movl $.LC0, 4(%esp) #3 ; store address of format string to stack
movl $1, (%esp) #4 ; store "flag" argument to __printf_chk to stack
call __printf_chk ; call routine
leave ; epilogue
ret ; epilogue
编译器有效地重写了这个:
printf("max unsigned long long = %llu\n", (unsigned long long)(pow(2, 64) - 1));
...进入这个:
__printf_chk(1, "max unsigned long long = %llu\n", 0xffffffffffffffffULL);
...并且在运行时,调用的堆栈布局如下所示(将堆栈显示为 32 位字,地址从图的底部向上递增):
: :
: Stack :
: :
+-----------------+
%esp+12 | 0xffffffff | \
+-----------------+ } <-------------------------------------.
%esp+8 | 0xffffffff | / |
+-----------------+ |
%esp+4 |address of string| <---------------. |
+-----------------+ | |
%esp | 1 | <--. | |
+-----------------+ | | |
__printf_chk(1, "max unsigned long long = %llu\n", |
0xffffffffffffffffULL);