看過 openwatcom c/pascal call convention, 接著看 linux 上最常用的 gcc, 它是如何
x86 push 指令會先將 %esp - 4 (32bit 環境) 然後將某個值複製到 %esp - 4 的位址。
ex:
%esp = 0xfff4
pushl $5
%esp - 4
copy $5 %esp (0xfff0)
asm_func 是一個組合語言 function, 但使用 c 語言 call convention, 所以可以被 c 呼叫用來傳遞 c 參數, 就像這樣:
void asm_func(char c, int i, const char *ptr); asm_func('a', 9, (char*)(0x1234));不過這篇的主題不談這個。
3 push %ebp 4 mov %esp,%ebp
25 mov %ebp, %esp 26 pop %ebp前後這段只是反向操作, 應該沒什麼問題。不過我的程式並沒有用到 %ebp, 為何要這樣做呢?原因是會有用到的時候, 有機會再談。這篇的主題一樣不談這個。
13 pushl $addr 14 pushl $56 15 pushl $'z' 16 pushl $fmt 18 call printf 19 addl $16, %esp
x86 c calling conventions 由右往左把 $addr, $56, $'z', $fmt 複製到 stack 中, 以 c 來看 L13 ~ L18 就像這樣:
printf("%c %d %p\n", 'z', 56, (char*)(0x80497be) );
pushl 4 次, 自然在 printf 執行後, 要把 esp 加回來 4+4+4+4 = 16, 這就是很多書上提到的 c 語言的 call convention。呼叫的一方負責把 stack 復原。但這些書沒提到的是 ...
看看 gcc 反組譯的結果, 令人驚訝, 看不到上述的結果, 而是:
5 sub $0x28,%esp 6 7 movl $addr,0xb(%esp) 8 movl $56,0x8(%esp) 9 movl $'z',0x4(%esp) 10 movl $fmt, (%esp) 18 call printf
呼叫的一方沒有將 stack 恢復 (沒有這樣的程式碼 addl $16, %esp), 其實不能這麼說, 而是呼叫的一方並沒有更動 stack pointer 自然不需要回復。
L5 先將 esp 留出 0x28 byte 空間, 再使用 L7 - L10 的方式將參數填入 stack, 一樣照右至左的順序, 這是書上沒提的部份, 我被這想法困擾很久了。在用組合語言呼叫 C 的時候, 大部份都是使用 asm_func.S 的作法, 後面的作法總是讓我困惑, 這次終於搞懂了。
回頭提這個:
3 push %ebp 4 mov %esp,%ebp
25 mov %ebp, %esp 26 pop %ebp
gcc 也不是
like this:
3 push %ebp 4 mov %esp,%ebp 25 leave
這就是 gcc 產生的組合語言。asm_func.S 是我從 gcc 反組譯之後修改過來的, 然後加上 printf 這些測試程式碼。和 open watcom 比較起來, open watcom 的組合語言程式碼容易理解, 不過基本原理都是一樣的。
ref:
http://en.wikipedia.org/wiki/X86_calling_conventions
http://stackoverflow.com/questions/672268/fastcall-gcc-example
http://pcmanlin.blogbus.com/logs/57953276.html
http://m.newsmth.net/article/KernelTech/69
http://stackoverflow.com/questions/7760736/gcc-fastcall-function-definition
沒有留言:
張貼留言
使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。
我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。