使用来自维基百科的这个例子,其中DrawSquare()调用DrawLine(),
![]()
(请注意,此图表底部有高地址,顶部有低地址.)
任何人都可以解释我什么ebp,并esp在这方面?
从我看到的,我会说堆栈指针总是指向堆栈的顶部,而指针指向当前函数的开头?或者是什么?
编辑:我的意思是在Windows程序的上下文中
edit2:eip工作怎么样?
edit3:我有来自MSVC++的以下代码:
var_C= dword ptr -0Ch
var_8= dword ptr -8
var_4= dword ptr -4
hInstance= dword ptr 8
hPrevInstance= dword ptr 0Ch
lpCmdLine= dword ptr 10h
nShowCmd= dword ptr 14h
Run Code Online (Sandbox Code Playgroud)
所有这些似乎都是dwords,因此每个都占用4个字节.所以我可以看到从hInstance到4个字节的var_4之间存在差距.这些是什么?我认为它是返回地址,可以在维基百科的图片中看到?
(编者注:从迈克尔的答案中删除了长篇引文,该答案不属于该问题,但编辑后续问题):
这是因为函数调用的流程是:
* Push parameters (hInstance, etc.)
* Call function, which pushes return address
* Push ebp
* Allocate space for locals
Run Code Online (Sandbox Code Playgroud)
我的问题(最后,我希望!)现在是,从我想要调用到prolog结尾的函数的参数弹出的瞬间发生了什么?我想知道ebp,esp是如何在那些时刻发展的(我已经理解了prolog是如何工作的,我只是想知道在我将参数推到堆栈之后和prolog之前发生了什么).
我让Google告诉我该gcc选项的含义-fomit-frame-pointer,它将我重定向到以下声明.
-fomit帧指针
不要将帧指针保存在寄存器中以查找不需要的函数.这避免了保存,设置和恢复帧指针的指令; 它还在许多功能中提供额外的寄存器.它还使某些机器无法进行调试.
根据我对每个函数的了解,将在进程内存的堆栈中创建激活记录,以保留所有局部变量和更多信息.我希望这个帧指针意味着一个函数的激活记录的地址.
在这种情况下,什么是函数类型,它不需要将帧指针保持在寄存器中?如果我得到这个信息,我会尝试设计基于它的新函数(如果可能),因为如果帧指针没有保存在寄存器中,一些指令将在二进制中省略.在具有许多功能的应用程序中,这将显着提高性能.
省略帧指针时是否有任何实质性的优化?如果我通过阅读理解正确的话这个页面,-fomit-frame-pointer在使用时我们要避免保存,设置和恢复帧指针.
这是仅针对每个函数调用完成的吗?如果是这样,是否真的值得为每个函数避免一些指令?优化不是一件容易的事.除调试限制外,使用此选项的实际含义是什么?
我使用和不使用此选项编译了以下C代码
int main(void)
{
int i;
i = myf(1, 2);
}
int myf(int a, int b)
{
return a + b;
}
Run Code Online (Sandbox Code Playgroud)
,
# gcc -S -fomit-frame-pointer code.c -o withoutfp.s
# gcc -S code.c -o withfp.s
Run Code Online (Sandbox Code Playgroud)
.
diff -u 这两个文件显示以下汇编代码:
--- withfp.s 2009-12-22 00:03:59.000000000 +0000
+++ withoutfp.s 2009-12-22 00:04:17.000000000 +0000
@@ -7,17 +7,14 @@
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
- pushl %ebp
- movl %esp, %ebp
pushl %ecx
- subl $36, …Run Code Online (Sandbox Code Playgroud) 看看这两个功能:
void function1() {
int x;
int y;
int z;
int *ret;
}
void function2() {
char buffer1[4];
char buffer2[4];
char buffer3[4];
int *ret;
}
Run Code Online (Sandbox Code Playgroud)
如果我打破function1()了gdb,并打印变量的地址,我得到这个:
(gdb) p &x
$1 = (int *) 0xbffff380
(gdb) p &y
$2 = (int *) 0xbffff384
(gdb) p &z
$3 = (int *) 0xbffff388
(gdb) p &ret
$4 = (int **) 0xbffff38c
Run Code Online (Sandbox Code Playgroud)
如果我做同样的事情function2(),我得到这个:
(gdb) p &buffer1
$1 = (char (*)[4]) 0xbffff388
(gdb) p &buffer2
$2 = (char (*)[4]) …Run Code Online (Sandbox Code Playgroud) 有一些调用约定(例如pascal,stdcall),但就我而言,C 确实使用cdecl(C 声明)。这些约定中的每一个在调用者将参数加载到堆栈上的方式上都略有不同,分别是由哪个(调用者/被调用者)进行清理。
谈到清理,这是我的问题。我不明白:有三种不同的东西吗?
或者我应该怎么看他们?
此外,这个问题的目标基本上是可变参数函数如何在像 Pascal 这样的调用约定中工作,或者stdcall被调用者应该在哪里清除/清理/恢复(我不知道哪个操作)堆栈 - 但他不知道有多少参数它会收到。
编辑
为什么将参数压入堆栈的顺序如此重要?您仍然拥有第一个参数(不是来自省略号的稳定参数),它为您提供有关 - 例如 - 变量参数数量的信息。并且还有“监护人”,它可以添加到省略号标点符号中,并且可以用作独立于调用约定的变量部分结束的标记。在这个链接中,如果调用者和被调用者在搞乱它们之前都保存了它们的状态,那么为什么调用者和被调用者都应该恢复这些寄存器的值?不应该只有其中一个(例如调用者)在调用函数之前将它们保存在堆栈中,仅此而已?另外,在同一个链接上
“因此,堆栈指针 ESP 可能会上下移动,但 EBP 寄存器保持固定。这很方便,因为这意味着我们始终可以将第一个参数称为 [EBP + 8],而不管在功能。”
推送的变量和局部变量在内存中是连续的。使用 EBP 推荐他们的优势在哪里?即使堆栈大小发生变化,它们之间也永远不会有一些动态偏移。
我读过的材料之一是这个站点(只是开始),以便更好地了解堆栈帧到底是什么。然后我继续 yt 并找到了这些堆栈概述和调用堆栈教程,但他们不知何故错过了我需要的部分。当你调用函数时到底发生了什么(我不明白指令“调用地址”后跟下一个指令a push值到堆栈上,这意味着返回值)。谁来控制退货地址?呼叫者,召集者?被叫方?当被调用者返回时,程序继续执行一条指令,该指令是从寄存器中读取操作或什么?
我正在编写一个 JIT 编译器,我惊讶地发现在 Win64 调用约定中,如此多的 x86-64 寄存器是非易失性的(被调用者保留的)。在我看来,非易失性寄存器只是在所有可以使用这些寄存器的函数中做更多的工作。在数值计算的情况下尤其如此,您希望在叶函数中使用许多寄存器,例如某种高度优化的矩阵乘法。但是,例如,16 个 SSE 寄存器中只有 6 个是易失性的,因此如果您需要使用更多,您将有很多溢出要做。
所以是的,我不明白。这里有什么权衡?
我在IDA的RUNTIME_FUNCTION结构的.pdata段中找到了一个大数组。因此,在哪里可以找到信息:从它的编译,如何创建以及如何在C ++中使用信息。请给我书籍,或提供具有良好描述和教程的链接,以使用此结构来处理异常和消除异常。
我正在研究一个用 c 编写的模拟问题,我的程序的主要部分是一个递归函数。当递归深度达到大约 500000 时,似乎发生堆栈溢出。
Q1 : 我想知道这正常吗?
Q2:一般有多少递归函数调用会导致堆栈溢出?
Q3:在下面的代码中,删除局部变量neighbor可以防止堆栈溢出吗?
我的代码:
/*
* recursive function to form Wolff Cluster(= WC)
*/
void grow_Wolff_cluster(lattic* l, Wolff* wolff, site *seed){
/*a neighbor of site seed*/
site* neighbor;
/*go through all neighbors of seed*/
for (int i = 0 ; i < neighbors ; ++i) {
neighbor = seed->neighbors[i];
/*add to WC according to the Wolff Algorithm*/
if(neighbor->spin == seed->spin && neighbor->WC == -1 && ((double)rand() / RAND_MAX) < …Run Code Online (Sandbox Code Playgroud)