【问题标题】:Why does function's argument get written to main stack frame and not function stack frame?为什么函数的参数被写入主堆栈帧而不是函数堆栈帧?
【发布时间】:2016-03-13 04:41:57
【问题描述】:

所以我在 gdb 中反汇编了这段 C 代码:

0x08048474 <main+0>: push ebp
0x08048475 <main+1>: mov ebp,esp
0x08048477 <main+3>: sub esp,0x8
0x0804847a <main+6>: and esp,0xfffffff0
0x0804847d <main+9>: mov eax,0x0
0x08048482 <main+14>: sub esp,eax
0x08048484 <main+16>: cmp DWORD PTR [ebp+8],0x1
0x08048488 <main+20>: jg 0x80484ab <main+55>
0x0804848a <main+22>: mov eax,DWORD PTR [ebp+12]
0x0804848d <main+25>: mov eax,DWORD PTR [eax]
0x0804848f <main+27>: mov DWORD PTR [esp+4],eax
0x08048493 <main+31>: mov DWORD PTR [esp],0x80485e5
0x0804849a <main+38>: call 0x804831c <printf@plt>
0x0804849f <main+43>: mov DWORD PTR [esp],0x0
0x080484a6 <main+50>: call 0x804833c <exit@plt>
0x080484ab <main+55>: mov eax,DWORD PTR [ebp+12]
0x080484ae <main+58>: add eax,0x4
0x080484b1 <main+61>: mov eax,DWORD PTR [eax]
0x080484b3 <main+63>: mov DWORD PTR [esp],eax
0x080484b6 <main+66>: call 0x8048414 <check_authentication>
0x080484bb <main+71>: test eax,eax
0x080484bd <main+73>: je 0x80484e5 <main+113>
0x080484bf <main+75>: mov DWORD PTR [esp],0x80485fb
0x080484c6 <main+82>: call 0x804831c <printf@plt>
0x080484cb <main+87>: mov DWORD PTR [esp],0x8048619
0x080484d2 <main+94>: call 0x804831c <printf@plt>
0x080484d7 <main+99>: mov DWORD PTR [esp],0x8048630
0x080484de <main+106>: call 0x804831c <printf@plt>
0x080484e3 <main+111>: jmp 0x80484f1 <main+125>
0x080484e5 <main+113>: mov DWORD PTR [esp],0x804864d
0x080484ec <main+120>: call 0x804831c <printf@plt>
0x080484f1 <main+125>: leave
0x080484f2 <main+126>: ret
End of assembler dump.

我的主要问题围绕以下几行展开:

0x080484b3 <main+63>: mov DWORD PTR [esp],eax
0x080484b6 <main+66>: call 0x8048414 <check_authentication>

当我在这个时间点通过 esp = 0xbffff7e0 时。当我进入 check_authentication 函数 esp = 0xbffff7a0.这些行正在写入作为 check_authentication 参数的 (char *) 的地址,但它们正在将地址写入 0xbffff7e0,而不是在 0xbffff7a0 - 0xbffff7e0 的堆栈帧内。我能想到的唯一理由是堆栈在创建时分配了填充,因为这可能是填充编译器这样做是为了节省空间?有谁知道是不是这样?为什么不写esp-4的地址,实际上是在函数的栈帧里面?

编辑:添加内存输出以帮助我解释错误

main() 内部:

(gdb) x/4xw $esp
0xbffff7e0: 0xb8000ce0 0x08048510 0xbffff848 0xb7eafebc

check_authentication() 内部:

(gdb) x/32xw $esp
0xbffff7a0: 0x00000000 0x08049744 0xbffff7b8 0x080482d9
0xbffff7b0: 0xb7f9f729 0xb7fd6ff4 0xbffff7e8 0x00000000
0xbffff7c0: 0xb7fd6ff4 0xbffff880 0xbffff7e8 0xb7fd6ff4
0xbffff7d0: 0xb7ff47b0 0x08048510 0xbffff7e8 0x080484bb
0xbffff7e0: 0xbffff9b7 0x08048510 0xbffff848 0xb7eafebc

在主 0xbffff7e0 = 0xe00c00b8

在 check_authentication 0xbffff7e0 = 0xb7f9ffbf

更改发生在跳转到 check_authentication 之前,但更改是针对 check_authentication 的,所以为什么它在 mains() esp 指向的 0xbffff7e0 处,而不是在 check_function 的堆栈帧内。我绞尽脑汁想弄明白。

【问题讨论】:

  • 我的程序集很糟糕,所以我可能错了,但它没有将指针移动到 CPU 寄存器 (eax) 并在那时处理 CPU 寄存器的数据副本吗?如果数据在寄存器上,那么在调试的时候你是看不到有效值的(反正不是通过检查存储在 RAM 中的值)。
  • 它将eax指向ino的指针值移动到esp指向的内存位置0xbffff7e0
  • 在 x86 上,如果一个函数分配的堆栈帧比必要的大,那也没关系。在这里,它使用 mov 将 args 存储到堆栈的顶部 n 位置,调用一个函数,然后继续而不做任何弹出。当它调用另一个函数时,它会做同样的事情。这使用较少的指令,因为它跳过了所有弹出(或添加 4 或 8 或其他任何内容)。从被调用者的角度来看,堆栈是完全正确的;它不关心它的调用者是否使用了推送或移动。
  • 感谢@MarkPlotnick,我认为这与节省指令有关。

标签: c gcc stack gdb


【解决方案1】:

没有“x86 中分配的堆栈帧”。栈是一个连续的内存区域,SP指向“栈顶”(它的最低地址)。 程序集不是存储在某个随机地址,而是在[ESP],因此它位于函数的堆栈帧内 - 在 EBP 和当前 ESP 包含之间。


PUSH 操作码的定义与以下 C 语句类似:

*--SP = value;   // decrease pointer first, then store

POP

value = *SP++;   // take the value pointed to, then increase pointer

现在在CALL check_authentication 编译器之前的这一行中,不是生成PUSHPOP 对,而是相当于

*SP = value;

【讨论】:

  • 它存储在当前堆栈帧中,但它存储的是用于下一个堆栈帧的地址。
  • SP 指向最上面的项目。当调用 check_authentication 时,这是该函数的第一个参数。
猜你喜欢
  • 2013-10-12
  • 1970-01-01
  • 2021-11-14
  • 1970-01-01
  • 2019-03-07
  • 2020-06-01
  • 1970-01-01
  • 2016-03-29
相关资源
最近更新 更多