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程序真正的跨平台了。