LLVM 中的區段堆疊¶
簡介¶
區段堆疊允許以遞增方式配置堆疊空間,而不是在執行緒初始化時配置一個整體區塊(具有某種最糟情況的大小)。這是透過配置堆疊區塊(以下稱為「堆疊區塊」)並將其連結到雙向鏈結串列來完成的。函式序言負責檢查目前的堆疊區塊是否有足夠的空間供函式執行;如果沒有,則呼叫 libgcc 執行階段以配置更多堆疊空間。區段堆疊是使用 LLVM 函式上的 "split-stack"
屬性來啟用的。
執行階段功能已存在於 libgcc 中。
實作細節¶
配置堆疊區塊¶
如上所述,函式序言會檢查目前的堆疊區塊是否有足夠的空間。目前的方法是在 TCB 中使用一個 slot 來儲存目前的堆疊限制(減去配置新區塊所需的空間量) - 這個 slot 的偏移量也是由 libgcc
決定。產生的組譯程式碼在 x86-64 上看起來像這樣
leaq -8(%rsp), %r10
cmpq %fs:112, %r10
jg .LBB0_2
# More stack space needs to be allocated
movabsq $8, %r10 # The amount of space needed
movabsq $0, %r11 # The total size of arguments passed on stack
callq __morestack
ret # The reason for this extra return is explained below
.LBB0_2:
# Usual prologue continues here
堆疊上函式引數的大小需要傳遞給 __morestack
(這個函式在 libgcc
中實作),因為需要將這些位元組從先前的堆疊區塊複製到目前的堆疊區塊。這是為了讓函式引數的 SP(和 FP)相對定址按預期運作。
需要不尋常的 ret
指令,以便呼叫 __morestack
的函式能夠正確返回。__morestack
不是返回,而是呼叫 .LBB0_2
。這是可能的,因為 ret
指令的大小和呼叫 __morestack
的 PC 都是已知的。當函式主體返回時,控制權會轉移回 __morestack
。__morestack
然後解除配置新的堆疊區塊,還原正確的 SP 值,並執行第二次返回,將控制權返回給正確的呼叫者。
變數大小的 alloca¶
關於 分配堆疊區塊 的章節自動假設每個堆疊框架的大小都是固定的。然而,LLVM 允許使用 llvm.alloca
內建函式在堆疊上分配動態大小的記憶體區塊。當遇到這種可變大小的 alloca 時,會產生以下程式碼:
檢查目前的堆疊區塊是否有足夠的空間。如果有的話,就如同一般情況一樣,只需調整堆疊指標 (SP)。
如果沒有足夠的空間,則產生對
libgcc
的呼叫,從堆積中分配記憶體。
從堆積分配的記憶體會鏈結到目前堆疊區塊中的清單中,並在堆疊區塊釋放時一併釋放。這可以防止記憶體洩漏。