[英]GCC calling convention for x86_64 Linux systems
我編寫了一個最小函數來測試我是否可以調用/鏈接 C 和 x86_64 匯編代碼。
這是我的main.c
#include <stdio.h>
extern int test(int);
int main(int argc, char* argv[])
{
int a = 10;
int b = test(a);
printf("b=%d\n", b);
return 0;
}
這是我的test.asm
section .text
global test
test:
mov ebx,2
add eax,ebx
ret
我使用這個腳本構建了一個可執行文件
#!/usr/bin/env bash
nasm -f elf64 test.asm -o test.o
gcc -c main.c -o main.o
gcc main.o test.o -o a.out
我寫了test.asm
卻沒有任何真正的線索我在做什么。 然后我離開並做了一些閱讀,現在我不明白我的代碼是如何工作的,因為我說服自己不應該這樣。
以下是我認為這不起作用的原因列表:
eax
和ebx
傳遞的。 我不認為這是對的。ret
可能希望從某個地方獲取一個返回地址。 我很確定我沒有提供這個。我寫的東西產生了正確的輸出,這完全是僥幸嗎?
我對此完全陌生。 雖然我聽說過一些 x86 概念,但這是我第一次真正嘗試編寫一些概念。 必須從某個地方開始?
test:
; save old base pointer
push rbp ; sub rsp, 8; mov [rsp] rbp
mov rbp, rsp ; mov rbp, rsp ;; rbp = rsp
; initializes new stack frame
add rdi, 2 ; add 2 to the first argument passed to this function
mov rax, rdi ; return value passed via rax
; did not allocate any local variables, nothing to add to
; stack pointer
; the stack pointer is unchanged
pop rbp ; restore old base pointer
ret ; pop the return address off the stack and jump
; call and ret modify or save the rip instruction pointer
我不保存或恢復基指針(設置堆棧幀)。 我實際上不明白為什么需要這樣做,但是我看過的每個示例都是這樣做的。
那是不需要的。 嘗試使用-O3
編譯一些 C 代碼,然后您會發現它不會發生。
Linux 系統上 gcc 編譯器的調用約定應該是通過堆棧傳遞參數。 這里我假設參數是使用 eax 和 ebx 傳遞的。 我不認為這是對的。
那部分只是因為僥幸而起作用。 該程序集恰好也按照您編譯的方式將10
放入eax
中,但不能保證這種情況總是會發生。 再一次,用-O3
編譯,它就不會了。
ret 可能希望從某個地方獲取一個返回地址。 我很確定我沒有提供這個。
這部分很好。 返回地址由調用者提供。 當你的函數被輸入時,它總是在堆棧的頂部。
甚至可能還有其他我不知道的原因。
是的,還有一個: ebx
已保存呼叫,但您正在破壞它。 如果調用函數(或堆棧中高於它的任何東西)使用它,那么這會破壞它。
我寫的東西產生了正確的輸出,這完全是僥幸嗎?
是的,因為上面的第二點和第四點。
作為參考,這里是gcc
在-O0
(默認優化級別)和-O3
下從您的 C 代碼生成的程序集的比較: https ://godbolt.org/z/7P13fbb1a
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.