我写了一个简单的C程序来查看printf()的堆栈框架
#include <stdio.h>
int main(void){
printf("%s");
}
我认为堆栈的工作方式是main()首先将"%s"推到堆栈上,因此printf将seg fault或打印出垃圾。但是,在我的反汇编中,没有任何地方会将"%s"推到堆栈上。我打印出了介于%fp和%sp之间的所有值,但没有一个包含"%s"。
主组件转储:
0x00400950 <+0>: lui gp,0x2
0x00400954 <+4>: addiu gp,gp,-32224
0x00400958 <+8>: addu gp,gp,t9
0x0040095c <+12>: addiu sp,sp,-32
0x00400960 <+16>: sw ra,28(sp)
0x00400964 <+20>: sw s8,24(sp)
0x00400968 <+24>: move s8,sp
0x0040096c <+28>: sw gp,16(sp)
0x00400970 <+32>: lw v0,-32744(gp)
0x00400974 <+36>: nop
0x00400978 <+40>: addiu v0,v0,2864
0x0040097c <+44>: move a0,v0
0x00400980 <+48>: lw v0,-32688(gp)
0x00400984 <+52>: nop
0x00400988 <+56>: move t9,v0
0x0040098c <+60>: jalr t9
0x00400990 <+64>: nop
0x00400994 <+68>: lw gp,16(s8)
0x00400998 <+72>: move sp,s8
0x0040099c <+76>: lw ra,28(sp)
0x004009a0 <+80>: lw s8,24(sp)
0x004009a4 <+84>: addiu sp,sp,32
0x004009a8 <+88>: jr ra
0x004009ac <+92>: nop
如果"%s"没有存储在堆栈上,它存储在哪里?此外,它从哪里获得要打印的相应字符串?
据我所知,mips-arch使用a0~a3作为函数调用的前四个参数。
通常,在实现级别将发生的事情是"%s"
字符串文字位于某种静态存储中。当调用printf
时,指向该字符串的指针将作为参数传递。这并不一定意味着这个指针被推到堆栈上。参数的传递方式取决于参数传递约定。它可以加载到寄存器中。
在您的特殊情况下,这里是"%s"
准备通过的地方:
0x00400970 <+32>: lw v0,-32744(gp)
0x00400974 <+36>: nop
0x00400978 <+40>: addiu v0,v0,2864
0x0040097c <+44>: move a0,v0
首先,从相对于全局指针寄存器的数据区域加载基地址。然后将该基地址偏移2864以获得CCD_ 4的地址。然后,地址被移动到a0
,寄存器v0
被重新用于计算printf
的地址(由于它在共享库中,这一事实使其变得复杂)。
既然"%s"
没有相应的char *
自变量,那么行为当然是形式上未定义的。但实际行为是什么?
实际行为可能是printf
将尝试以某种方式提取char *
指针,可能是从堆栈中提取。(变元函数的尾部参数通常只放在堆栈中。)
现在,由于调用者没有在那里放置参数,printf
提取一些"垃圾"字,并将其视为char *
,将该字所指向的内存打印为以null结尾的字符串。也就是说,如果该单词指向有效内存。
如果您的目标是转储一些字节的堆栈内存,那么这根本不可靠。您不知道什么样的值作为char *
指针得到解释器,也不知道它指向什么,或者它是否指向任何东西,更不用说堆栈了。
伪char *
本身可能是从堆栈中提取的,但实际上您并没有打印该指针本身。
以下可能会为您获取几个字节的堆栈:
printf("%pn");
同样,任何没有参数的数字转换也可能如此。原因是%p
与%s
不同,实际上打印指针本身。如果%p
的自变量值是从堆栈中提取的,则该值的打印表示会泄露堆栈中一小部分的一些信息。
.file "1.c"
.section .rodata
.LC0:
.string "%s"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $.LC0, %edi
movl $0, %eax
call printf
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010"
.section .note.GNU-stack,"",@progbits
我使用gcc生成程序集。字符串的存储方式与您想象的不同。字符串是静态存储的。