Windows平臺NASM彙編與C混合調用

Windows平臺NASM彙編與C混合調用

tonyblackwhite

之前介紹了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文件需要用到的函數到了這裏都加入了前導下劃線。

輸入如下編譯指令,運行,可得結果:

IMG_257

在這個簡單的例子中,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;

}

輸入如下編譯指令,運行,可得結果:

IMG_258

上圖中,出現了那句警告!!!

別怕,這句話沒有錯!在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

顯然,去掉了所有_下劃線,現在使用如下指令編譯、運行:

IMG_259

你看,在nasm指令中增加一句--prefix _就解決了NASM彙編的可移植性問題。

現在,NASM程序完全具有可移植性了(Linux和Widnows平臺通用了),美觀度也大大增加了。其實彙編語言也挺好看的,哈哈!

本文就是在Windows平臺,如何使用GCC和NASM混合編程的例子。

注意,本文的方法,使得NASM程序真正的跨平臺了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章