【问题标题】:What is between ESP and EBP?ESP和EBP之间是什么?
【发布时间】:2013-02-07 20:39:35
【问题描述】:

是的,我相信这已经被隐含地回答了很多次,但我似乎无法完全理解它。

如果您有一个 (x86) 堆栈跟踪(例如,在 WinDbg 中查看它),并且您查看寄存器,那么 EBP 和 ESP 值相隔 x 字节意味着什么?

链接:

举一个我最近的堆栈跟踪的例子:

0:016> k
ChildEBP RetAddr  
1ac5ee8c 76b831bb ntdll!NtDelayExecution+0x15
1ac5eef4 76b83a8b KERNELBASE!SleepEx+0x65
1ac5ef04 0060e848 KERNELBASE!Sleep+0xf
1ac5ef10 76859d77 MyApp!application_crash::CommonUnhandledExceptionFilter+0x48 [...\applicationcrash.inc.cpp @ 47]
1ac5ef98 775a0df7 kernel32!UnhandledExceptionFilter+0x127
1ac5efa0 775a0cd4 ntdll!__RtlUserThreadStart+0x62
1ac5efb4 775a0b71 ntdll!_EH4_CallFilterFunc+0x12
1ac5efdc 77576ac9 ntdll!_except_handler4+0x8e
1ac5f000 77576a9b ntdll!ExecuteHandler2+0x26
1ac5f0b0 7754010f ntdll!ExecuteHandler+0x24
1ac5f0b0 6e8858bb ntdll!KiUserExceptionDispatcher+0xf
1ac5f400 74e68ed7 mfc80u!ATL::CSimpleStringT<wchar_t,1>::GetString [f:\dd\vctools\vc7libs\ship\atlmfc\include\atlsimpstr.h @ 548]
1ac5fec0 6e8c818e msvcr80!_NLG_Return [F:\dd\vctools\crt_bld\SELF_X86\crt\prebuild\eh\i386\lowhelpr.asm @ 73]
1ac5ff48 74e429bb mfc80u!_AfxThreadEntry+0xf2 [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\thrdcore.cpp @ 109]
1ac5ff80 74e42a47 msvcr80!_callthreadstartex+0x1b [f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c @ 348]
1ac5ff88 76833677 msvcr80!_threadstartex+0x66 [f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c @ 326]
1ac5ff94 77569f02 kernel32!BaseThreadInitThunk+0xe
1ac5ffd4 77569ed5 ntdll!__RtlUserThreadStart+0x70
1ac5ffec 00000000 ntdll!_RtlUserThreadStart+0x1b

0:016> r
eax=00000000 ebx=1ac5efc8 ecx=19850614 edx=00000000 esi=1ac5eed0 edi=00000000
eip=7754fd21 esp=1ac5ee8c ebp=1ac5eef4 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206

ESP 1ac5ee8c 的值 - EBP 1ac5eef4 = 104 字节差异。那么里面有什么?

【问题讨论】:

    标签: debugging visual-c++ x86 cpu-registers stack-frame


    【解决方案1】:

    通常,该空间是为最终存储在堆栈中的局部变量保留的。在函数的开始处,ESP 递减适当的值。

    在你的例子中,函数中有 104 个字节的本地变量。

    【讨论】:

    • NtDelayExecution 的 104 字节确实令人惊叹……但话又说回来,可能显示的调用堆栈已损坏……
    • 嗯,104 字节并不算多,尤其是如果NtDelayExecution() 出于某种原因在堆栈上分配了一个字符串缓冲区。
    • 我交叉检查了。 都没有 SleepEx 和 NtDelayExecution 都没有 push EBP ,尤其是 SleepEx 似乎很重要。所以在我的例子中,EBP 仍然指向Sleep 堆栈位置。
    【解决方案2】:

    ESP 是当前堆栈指针。 EBP 是当前栈帧的基指针。

    当您调用函数时,通常会在堆栈上为局部变量保留空间。该空间通常通过 EBP 引用(在函数调用期间,所有局部变量和函数参数都是从该寄存器的已知常量偏移量。)另一方面,ESP 将在函数调用期间随着其他函数的调用而改变,或者作为临时堆栈空间用于部分运算结果。

    请注意,如今大多数编译器都可以选择通过 ESP 引用所有局部变量。这释放了 EBP 以用作通用寄存器。

    通常,当您查看函数顶部的反汇编代码时,您会看到如下内容:

    push EBP
    mov  EBP, ESP
    sub  ESP, <some_number>
    

    因此 EBP 将指向此帧的堆栈顶部,而 ESP 将指向堆栈上的下一个可用字节。 (堆栈通常 - 但不是必须 - 在内存中增长。)

    【讨论】:

    • 写。在我的示例中,我在调试器中检查了对睡眠的调用:SleepExNtDelayExecution 都没有 push EBP,尤其是 SleepEx 似乎很重要。因此,在我的示例中,EBP 仍然指向 Sleep 堆栈位置,两个调用函数中的所有内容也在堆栈上。
    • 是的,可能大多数 WinAPI 函数都是使用编译器选项构建的,该选项可以释放 EBP 以供一般用途使用。即编译器跟踪它在整个函数中修改了多少ESP,并根据需要使用不同的偏移量引用相对于ESP的局部变量。在这种情况下,无法知道 EBP-ESP 是否准确地反映了当前堆栈帧的大小。很可能不会。
    • 什么是some_number?不总是 0?
    • @zoujyjs some_number 取决于框架中所需的存储量,而所需的量取决于函数中发生的事情。
    猜你喜欢
    • 2011-08-23
    • 2013-01-29
    • 1970-01-01
    • 2014-05-15
    • 2011-01-31
    • 1970-01-01
    • 1970-01-01
    • 2011-05-08
    相关资源
    最近更新 更多