2013-11-22 44 views
0

我想在函数调用/返回期间使用下面的代码片段分析Linux程序集堆栈初始化/清除。未初始化的变量是有意的。有关Linux函数堆栈初始化的问题

#define MAX 16 

typedef struct _CONTEXT { 
    int arr[MAX]; 
    int a; 
    int b; 
    int c; 
}; 

void init(CONTEXT* ctx) 
{ 
    memset(ctx->arr, 0, sizeof(ctx->arr[0])); 
    ctx->a = 1; 
} 

void process(CONTEXT* ctx) 
{ 
    int trash; 
    int i; 
    for (i = 0; i < MAX; i++) 
    { 
     trash = ctx->arr[i]; 
    } 
} 

int main(int argc, char *argv[]) 
{ 
    CONTEXT ctx; 
    init(&ctx); 
    process(&ctx); 
    return 0; 

} 

当我从学校了解到,从本次讲座slide
函数的堆栈初始化(风格-1)的组装应该是这样的:

pushq %rbp 
movq %rsp, %rbp 
subq $16, %rsp 
movq %rdi, -8(%rbp) 
... 
leave 
ret 

但是,当我使用gcc编译上面的代码片段,函数maininit具有相同的堆栈初始化例程style-1包括subq指令来分配堆栈变量的内存空间,
但函数process没有这种堆栈初始化。
我得到这个汇编代码(风格-2):

pushq %rbp 
movq %rsp, %rbp 
movq %rdi, -24(%rbp) 
... 
popq %rbp 
ret 

所以问题是:

  1. 什么是在编译时产生不同的功能堆栈初始化的编译器决定的政策?我没有在这个代码中放入任何__cdecl等,但是找到了2个不同的堆栈初始化。

  2. 如何初始化函数初始化时分配的堆栈内存地址和大小?

  3. movq %rdi, -8(%rbp)的用途是什么?

  4. 是否有旁更多的堆栈初始化款式风格-1风格-2在Linux呢?
    (没有明确提到__cdecl__stdcall东西)

+0

你是如何编译它的?你使用优化设置吗?如果是,那么输出可能会有所不同,具体取决于函数中真正使用的内容。尤其是像这样的例子,这些例子大部分都被删除了。 – Devolus

+0

我刚刚gcc-ed没有优化,所以默认优化(-O2?)是我的设置我猜。 – LocustSpectre

+0

我用-O0生成另一个程序集文件,但在两个程序集文件(默认和-O0)之间使用差异仅发现一个区别(-O0的存在)。 – LocustSpectre

回答

3

函数被编译的方式是非常具体的编译器,而不是特定的操作系统。我确定在32位Windows下由GCC生成的代码与在32位Linux下由GCC生成的代码类似,而在32位Linux下由Sun C编译器生成的代码与由GCC生成的代码看起来有所不同。这也是堆栈初始化的情况!因此,根据所使用的编译器,编译器设置,编译器版本,内部编译器状态等,可能有许多堆栈初始化样式。

您显然正在运行64位代码。与64位Windows和Linux中的32位Windows(其中__cdecl和__stdcall存在)不同,只有一种调用约定:在32位Linux中,这等同于Windows中的__cdecl; 64位Linux和64位Windows使用两种不同的基于寄存器的调用约定。这意味着:您无法更改Linux和64位Windows程序的调用约定,因为只支持一个。

movq %rdi, -8(%rbp)的目的是将参数(在rdi寄存器中)存储在堆栈上; movq %rdi, -24(%rbp)也一样,但是它正在写入可能被信号处理程序覆盖的堆栈的某个区域 - 这不是一个好主意!但是,如果不从堆栈中读取值,则这不成问题!

很明显,“style-2”函数不需要任何堆栈内存。

+0

谢谢。我不知道只有一个调用约定。我将深入研究现在存储的参数,以及一些书籍和谷歌 – LocustSpectre