2014-01-26 90 views
3

因此,似乎everyone知道OSX系統調用總是16字節堆棧對齊。太棒了,這是有道理的,當你有這樣的代碼:瞭解OSX 16字節對齊

section .data 
    message db 'something', 10, 0 

section .text 
    global start 

start: 
push 10   ; size of the message (4 bytes) 
push msg  ; the address of the message (4 bytes) 
push 1   ; we want to write to STD_OUT (4 bytes) 
mov  eax, 4  ; write(...) syscall 
sub  esp, 4  ; move stack pointer down to 4 bytes for a total of 16. 
int  0x80  ; invoke 
add  esp, 16 ; clean 

完美,堆棧對齊到16個字節,是非常有道理的。儘管我們稱之爲系統調用(1)(exit)。從邏輯上講,將是這個樣子:

push 69   ; return value 
mov  eax, 1 ; exit(...) syscall 
sub  esp, 12 ; push down stack for total of 16 bytes. 
int  0x80  ; invoke 

這不工作,雖然,但這:

push 69   ; return value 
mov  eax, 1 ; exit(...) syscall 
sub  esp, 4 ; push down stack for total of 8 bytes. 
int  0x80  ; invoke 

這工作正常,但是這隻有8個字節???? Osx很酷,但是這個ABI讓我瘋狂。有人可以闡明我不瞭解的東西嗎?

+0

您確定堆棧指針在您顯示的代碼之前是16字節對齊的嗎?另外,正如你所知,蘋果公司在系統調用級別不保持二進制兼容性,僅在系統庫級別。 –

+0

是的,事實上,我可以只寫最後一段代碼,它按預期工作。但它並沒有真正凝聚我所聽到的有關16字節對齊的所有信息。 – sircodesalot

回答

3

短版:你可能並不需要對齊到16個字節,你只需要你的參數列表之前,總是留下一個4字節的差距。

龍版本:

這就是我認爲正在發生的事情:我不知道這是真的,堆棧應該是16字節對齊。然而,邏輯指出如果是,並且如果需要填充或調整堆棧以實現該對齊,則必須在之前發生,系統調用的參數被推入,而不是之後。在int 0x80指令和實際參數的位置之間,堆棧指針之間不能有任意數量的字節。內核不知道在哪裏找到實際的參數。在之後從堆棧指針減去以實現「對齊」參數不會對齊參數,它通過在堆棧指針和參數之間插入任意數量的字節來對齊堆棧指針。不管別的什麼可能是真的,那是不對的。

那麼爲什麼第一個和第三個片段可以工作呢?他們不也在那裏插入任意字節嗎?他們偶然工作。這是因爲它們都碰巧插入了4個字節。這種調整不是「成功」的,因爲它實現了堆棧對齊,它是系統調用ABI的一部分。顯然,系統調用ABI期望並且要求在參數列表之前有一個4字節的插槽。

syscall()函數的源可以發現here。它看起來像這樣:

LEAF(___syscall, 0) 
    popl %ecx  // ret addr 
    popl %eax  // syscall number 
    pushl %ecx 
    UNIX_SYSCALL_TRAP 
    movl (%esp),%edx // add one element to stack so 
    pushl %ecx  // caller "pop" will work 
    jnb 2f 
    BRANCH_EXTERN(cerror) 
2: 
END(___syscall) 

要調用這個庫函數,調用者將已經設置堆棧指針指向參數給syscall()功能,這與系統調用號開始,然後有真正的論據實際的系統調用。但是,調用者然後將使用call指令來調用它,將返回地址壓入堆棧。

因此,上面的代碼彈出返回地址,將系統調用號碼彈出到%eax,將返回地址推回堆棧(系統調用號碼最初所在的位置),然後執行int 0x80。所以,堆棧指針指向返回地址,然後是參數。還有4個字節:返回地址。我懷疑內核忽略了返回地址。我猜它在系統調用ABI中的存在可能只是爲了使系統調用的ABI類似於函數調用。

這對系統調用的對齊需求意味着什麼?那麼,這個函數可以保證從調用者的設置改變棧的對齊方式。調用者可能設置了16字節對齊的堆棧,並且該函數在中斷之前將其移動4個字節。對於系統調用,堆棧需要16字節對齊可能只是一個神話。另一方面,調用系統庫函數的16字節對齊要求絕對是真實的。我開發的葡萄酒項目被它燒了。對於128位SSE參數數據類型來說,這是非常必要的,但是如果即使對於不使用這些參數的函數,如果alignemtn錯誤,蘋果也會讓他們的懶惰符號解析器故意炸燬,以便早日發現問題。系統調用不會受到早期失敗機制的影響。這可能是內核不需要16字節對齊。我不確定是否有系統調用需要128位參數。

+0

'你可能不需要對齊到16個字節,你只需要在你的參數列表之前總是留下一個4字節的空隙。「這幾乎是我的結論。 – sircodesalot

+0

是的,在這一點上,我非常確信這是事實。這是在這裏備份:http://michaux.ca/articles/assembly-hello-world-for-os-x。 – sircodesalot