2013-11-22 84 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