2016-03-07 26 views
0

到目前爲止,我一直在學習編寫一些x86_64程序集。我看你能減去RSP向下生長的堆棧和分配空間,所以我寫了下面的代碼:下面的x86_64程序集是如何讓我出現分段錯誤的?

push %rbp 
movq %rsp, %rbp 
subq $16, %rsp 
movq $200, -8(%rsp) 
movq $300, -16(%rsp) 
popq %rbp 
retq 

從我的理解,這將使一個函數在那裏建立了一個堆棧幀,然後它在堆棧上分配16個字節,並將值分別設置爲-8和-16,分別設置爲200和300。 但是,當我使用gcc運行時,出現了分段錯誤。雖然如果我刪除sub程序的一部分,它的作品完美。我假設我誤解了一些東西,那麼究竟發生了什麼?

+0

您需要釋放您分配的空間。通過將'rbp'移回'rsp'或通過撤消'sub'。 – Jester

+0

@Jester,所以我可以做相反的減法並將16添加到rsp? – mosmo

+0

是的,如果沒有使用幀指針,這是通常的方式。當你的情況確實有幀指針時,通常只是移回到'rsp'。 – Jester

回答

4

正如Jester所說,問題在於,當你使用pop %rbp/ret時,堆棧指針指向其他地方,所以你沒有得到舊的%rbp和返回地址。 (你永遠不會寫信給你彈出的位置,由於另一個潛在的錯誤,所以我不能確切地告訴你你的ret到底是哪一個無效的地址。)

如果你做一個堆棧幀(mov %rsp, %rbp),那麼使用相對於%rbp的偏移量是正常的。有趣的事實:movq $200, -8(%rbp)需要one byte less machine code比等效的movq $200, 8(%rsp)。 (使用%rsp作爲基址寄存器不幸總是需要一個SIB字節編碼的有效地址。)

使用%rbp也意味着引用如果你推/彈出東西,任何給定的堆棧地址也不會改變的表達(在32位代碼中常見,堆棧參數爲ABI,但在64位代碼中很少見,64位gcc在32位之前切換爲-fomit-frame-pointer)。


您的movq $200, -8(%rsp)使用您保留的16B以外的空間。這是我之前提到的「其他潛在的錯誤」。

當前%rsp下面使用高達128B實際上是在SysV的ABI的錯誤:異步事件(信號處理等)避免重挫的red zone,不調用任何其他功能都可以如此之小功能避免花費修改%rsp來預留空間。 x86-64有15個通用寄存器(不包括堆棧指針),因此中小型函數通常不需要使用堆棧,而是保存/恢復調用保存的寄存器。或者用於本地陣列。

Windows ABI不使用紅色區域,因此即使您不用call自己動手,也可能會潛在低於%rsp的內存。

有關調用約定/ ABI的鏈接,請參閱標記wiki。