Windows平臺NASM彙編與C混合調用
之前介紹了Windows平臺下,用微軟宏彙編MASM與C混合調用的方法。MASM是微軟獨有的,Linux沒法用,我喜歡學一個能夠應用於兩種平臺的,所以還是更鐘情於開源的可跨平臺的NASM彙編。
tonyblackwhite:Win平臺最簡單的方式實現C程序調用匯編函數6 贊同 · 0 評論文章
本文介紹Windows平臺NASM彙編與C混合調用的方法。
GCC不用多解釋,這是大名鼎鼎的C、C++的編譯器,簡直可以搞一切。
NASM是一個開源的x86的彙編器,用來編譯彙編的。
爲什麼要研究GCC和NASM的聯合編譯呢?GCC本來就能夠嵌套匯編代碼啊?這是因爲GCC中嵌套的彙編代碼是AT&T的彙編代碼不是Intel格式的彙編。AT&T彙編我不喜歡用,只喜歡用Intel彙編,也就是類似於MASM和NASM這樣子的。
下面我們結合一個例子來將二者如何聯合使用。
如果大家善用搜索,就會發現網上有大量的例子,講述了Linux平臺下,GCC和NASM聯合調用的例子。
那我還寫個什麼勁呢?原來,這些例子一旦應用到Windows平臺,幺蛾子就來了。
本文就是講述Windows平臺下,GCC和NASM聯合調用時如何滅掉那些幺蛾子的。
1、簡單例子
該例子包含兩個文件:test1.asm和test.c。
test1.asm的內容爲
extern _print_helloworld
[section .text]
global _print_two_hello_world
_print_two_hello_world:
call _print_helloworld
call _print_helloworld
test.c的內容爲
#include "stdio.h"extern void print_two_hello_world();char *strhello = "Hello,world!\n";
void print_helloworld(){
printf("%s", strhello);}
int main(){
print_two_hello_world();
return 0;}
這裏,在NASM彙編程序中,聲明瞭一個全局函數print_two_hello_world,由於這裏是Windows平臺,該全局函數就變成了_print_two_hello_world函數,即增加了前導的下劃線。這與Windows平臺的調用方式stdcall有關。
該彙編程序文件還聲明外部有一個函數_print_helloworld,這是由C程序提供的,同樣也增加了一個前導下劃線。
前導下劃線使得程序不美觀,而且它的加入使得該彙編程序無法應用於Linux平臺。後面會講述解決方法。
這裏,只需要記住了,在Win平臺下,所有C文件需要用到的函數到了這裏都加入了前導下劃線。
輸入如下編譯指令,運行,可得結果:
在這個簡單的例子中,C程序調用了NASM中的函數,而NASM彙編中有調用了C中的函數,而且在Windows平臺中,這些函數都加入了前導下劃線。這是與Linux平臺最大的不同。
這個例子就是簡單的C與彙編相互調用的例子,好像彙編也沒那麼難嘛!
2、複雜例子
該例子包含兩個文件:test1.asm和test.c。
test1.asm的內容爲
global _string
extern _strhello
extern _printf
[section .data]
_string:
db 'I am Chinese.',0x0A,0x0
[section .text]
global _print_hello
_print_hello:
push dword [_strhello]
call _printf
add esp,byte 4
ret
test.c的內容爲
#include "stdio.h"
#include "string.h"
extern char *string;
extern void print_hello();
char *strhello = "Hello,world!\n";
char *str = NULL;
int main(){
str = &string;
printf("%s", str);
print_hello();
return 0;
}
輸入如下編譯指令,運行,可得結果:
上圖中,出現了那句警告!!!
別怕,這句話沒有錯!在C語言中定義了一個strhello的字符串變量,在C語言中strhello表示的是字符串的首地址,比如字符串的地址是0xa00001,而strhello是個指針即4字節其地址爲0xb00001, 在C語言中strhello表示的值是 0xa00001 字符串的首地址,但到了NASM中則表示的 strhello變量的首地址了 0xb00001,所以彙編中用下面這個取出具體內存中的內容:
push dword [_strhello]
代碼中加了中括號表示是內容,這一點一定要注意,否則會出錯!!
另外,上面彙編中,所有的全局函數、引用函數、引用外部數據等都加了_下劃線。顯然,這還是Windwos平臺的特殊性帶來的。
關於win平臺下函數和變量增加_下劃線的思考:
如果nasm文件中僅僅是一兩個函數,那就像我之前做的那樣,可以直接在函數前面加下劃線,也不是不可以。這主要是Windows的stdcall方式爲函數名自動加前導的下劃線導致的。
如果是多個文件或者n個函數,這種手動添加前導下劃線的方式是不可取的,會增加工作量,而且容易出錯,此外還破壞了NASM的平臺可移植性,也破壞了NASM程序的美感。
那麼怎麼解決呢?可以在編譯時用–prefix給全局參數或者函數添加前綴,即nasm指令使用時增加如下附加指令:
--prefix _
下面改造例子2:
定義test2.asm的內容爲:
global string
extern strhello
extern printf
[section .data]
string:
db 'I am Chinese.',0x0A,0x0
[section .text]
global print_hello
print_hello:
push dword [strhello]
call printf
add esp,byte 4
ret
顯然,去掉了所有_下劃線,現在使用如下指令編譯、運行:
你看,在nasm指令中增加一句--prefix _就解決了NASM彙編的可移植性問題。
現在,NASM程序完全具有可移植性了(Linux和Widnows平臺通用了),美觀度也大大增加了。其實彙編語言也挺好看的,哈哈!
本文就是在Windows平臺,如何使用GCC和NASM混合編程的例子。
注意,本文的方法,使得NASM程序真正的跨平臺了。