![](/img/trans.png)
[英]GDB: how to interpret x86_64 call stack & registers (specifically $rbp)
[英]What is the purpose of the RBP register in x86_64 assembler?
所以我正在嘗試學習一點裝配,因為我需要它用於計算機體系結構類。 我寫了一些程序,比如打印Fibonacci序列。
我認識到每當我編寫一個函數時,我都會使用這三行(正如我從gcc
生成的匯編代碼與它的C
等價物進行比較所學到的):
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
我有2個問題:
%rbp
? 使用%rsp
是不是更簡單,因為它的內容被移動到第二行的%rbp
? %rsp
減去任何東西? 我的意思是它並不總是16
,當我printf
7或8個變量時,我會減去24
或28
。 我在虛擬機(4 GB RAM),Intel 64位處理器上使用Manjaro 64位
rbp
是x86_64上的幀指針。 在生成的代碼中,它獲取堆棧指針( rsp
)的快照,以便在對rsp
調整時(即為局部變量保留空間或push
值push
送到堆棧),仍可從本地變量和函數參數訪問與rbp
的恆定偏移量。
許多編譯器提供幀指針省略作為優化選項; 這將使生成的匯編代碼相對於rsp
訪問變量,並將rbp
釋放為另一個用於函數的通用寄存器。
在GCC的情況下,我猜測你正在使用AT&T匯編語法,那個開關是-fomit-frame-pointer
。 嘗試使用該開關編譯代碼並查看您獲得的匯編代碼。 您可能會注意到,當訪問相對於rsp
而不是rbp
值時,指針的偏移量會在整個函數中發生變化。
Linux使用System V ABI for x86-64(AMD64)架構; 有關詳細信息 ,請參閱OSDev Wiki上的System V ABI 。
這意味着堆棧會逐漸減少 ; 較小的地址在堆棧中“更高”。 典型的C函數編譯為
pushq %rbp ; Save address of previous stack frame
movq %rsp, %rbp ; Address of current stack frame
subq $16, %rsp ; Reserve 16 bytes for local variables
; ... function ...
movq %rbp, %rsp ; \ equivalent to the
popq %rbp ; / 'leave' instruction
ret
為局部變量保留的內存量始終是16個字節的倍數,以使堆棧保持對齊為16個字節。 如果局部變量不需要堆棧空間,則沒有subq $16, %rsp
或類似指令。
(注意,推送到堆棧的返回地址和前面的%rbp
都是8個字節,總共16個字節。)
%rbp
指向當前堆棧幀, %rsp
指向堆棧頂部。 因為編譯器知道函數內任何點上%rbp
和%rsp
之間的差異,所以可以自由地使用其中一個作為局部變量的基礎。
堆棧框架只是本地函數的操場:當前函數使用的堆棧區域。
每當使用優化時,當前版本的GCC都會禁用堆棧幀。 這是有道理的,因為對於用C編寫的程序,堆棧幀對調試最有用,但不是很多。 (但是,您可以使用例如-O2 -fno-omit-frame-pointer
來保持堆棧幀,否則啟用優化。)
雖然相同的ABI適用於所有二進制文件,但無論它們是用什么語言編寫的,某些其他語言都需要堆棧幀來“展開”(例如,向當前函數的祖先調用者“拋出異常”); 即,“展開”堆棧幀,可以中止一個或多個函數並將控制傳遞給某個祖先函數,而不會在堆棧上留下不需要的東西。
當省略堆棧幀 - 用於GCC的-fomit-frame-pointer
,函數實現基本上改變為
subq $8, %rsp ; Re-align stack frame, and
; reserve memory for local variables
; ... function ...
addq $8, %rsp
ret
因為沒有堆棧幀( %rbp
用於其他目的,並且它的值永遠不會被推送到堆棧),所以每個函數調用只將返回地址推送到堆棧,這是一個8字節的數量,所以我們需要減去來自%rsp
8來保持它是16的倍數。(通常,從%rsp
減去並添加到%rsp
是8的奇數倍。)
函數參數通常在寄存器中傳遞。 有關詳細信息,請參閱本答案開頭的ABI鏈接,但簡而言之,整數類型和指針在寄存器%rdi
, %rsi
, %rdx
, %rcx
, %r8
和%r9
%rcx
,並帶有浮點參數%xmm0
到%xmm7
寄存器。
在某些情況下,你會看到rep ret
而不是rep
。 不要混淆:在rep ret
意味着同樣的事情為ret
; rep
前綴雖然通常與字符串指令(重復指令)一起使用,但在應用於ret
指令時不執行任何操作。 只是某些AMD處理器的分支預測器不喜歡跳轉到ret
指令,推薦的解決方法是在那里使用rep ret
。
最后,我省略了堆棧頂部上方的紅色區域 (地址小於%rsp
的128個字節)。 這是因為它對於典型的函數並不真正有用:在普通的has-stack-frame情況下,你需要你的本地東西在堆棧框架內,以便進行調試。 在omit-stack-frame的情況下,堆棧對齊要求已經意味着我們需要從%rsp
減去8,因此包含該減法中局部變量所需的內存不需要任何成本。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.