孤独的猫

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

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

posted on 2024-04-28 13:53  孤独的猫  阅读(232)  评论(0编辑  收藏  举报