刚接触到C和汇编函数互相调用的例子,觉得很是神奇,灰常强大……感动得(谁叫咱是菜鸟)
C和汇编是两种编程语言,在语法上我觉得是差别很大的。个人认为,把它们联系起来的纽带是“C Calling Conversion”——C调用约定。下面是从书上看到的例子。
"foo.asm"文件代码:
- extern choose ; int choose(int a, int b);
-
- [section .data] ;
- num1st dd 3
- num2nd dd 4
- [section .text] ;
- global _start
- global myprint
-
- _start:
- push dword [num2nd] ; `.
- push dword [num1st] ; |
- call choose ; | choose(num1st, num2nd);
- add esp, 8 ; /
- mov ebx, 0
- mov eax, 1 ; sys_exit
- int 0x80 ;
-
- ; void myprint(char* msg, int len)
- myprint:
- mov edx, [esp + 8] ; len
- mov ecx, [esp + 4] ; msg
- mov ebx, 1
- mov eax, 4 ; sys_write
- int 0x80 ;
- ret
"bar.c" 文件内容:
- void myprint(char* msg, int len);
- int choose(int a, int b)
- {
- if(a >= b){
- myprint("the 1st one\n", 13);
- }
- else{
- myprint("the 2nd one\n", 13);
- }
- return 0;
- }
由上可知:
1.由于在bar.c中用到函数myprint(),所以要在foo.asm中用关键字global将其导出。并且要一个地方(bar.c能引用到的地方)“声明”void myprint(char *msg, int len)这个函数,就像我们平时使用纯C一样,在主函数main()之外,我们可以自定义函数,方法也可以是先给出一个子函数的声明,例如
void func(int a,int b);接着在主函数后面写出它的具体实现,如:
void func()
{
//something
}
在C里面,一切我们都觉得很自然,因为子函数有了,调用起来也简单,func()需要什么参数,我们调用的时候直接“填”就行了,调用完程序继续执行,完全不用考虑堆栈之类的东西(系统自动配栈),所以的事情都符合逻辑。
但是如果子函数或者说调用的函数是用汇编写的,情况就复杂了。函数符号要怎样识别,这里用了一个关键字“global”导出函数,这个还好理解,函数名我们可以认为是一个指针,一个地址,所以在哪里问题不大。我觉得最难理解的是“参数”。汇编里面是通过在call之前push 参数实现的,进入子程序后利用esp从堆栈中取得,在汇编程序调用中,堆栈非常重要,起传递参数的作用(当然,返回时也用到)。问题来了,在C中,我没有“push”,虽然我知道函数名,但是我怎样把参数传递给函数呢?正当我无限困惑时,上例的应用让我无比震惊,心理想,就这么简单,就这么随意就行了?!bar.c中“void myprint(char *msg, int len);”绝对是强大的。返回类型,参数类型,参数个数清清楚楚地给出了,咋一看,这不是C的子函数吗?堆栈,还是堆栈。虽然C里面不能push参数,但是根据C的调用约定,系统配栈时,会先后将 'len' 'msg'的地址入栈,效果和在汇编中的一样,这样一来在,C里面调用汇编里面的函数就没问题了。反过来,在汇编中调用C里面的函数也是一样的。要利用extern关键字导入,这个属于语法问题。个人认为,理解时要抓住三点:
1.不管是在C还是汇编里,调用函数本质上是 call 一个地址,光这个操作和参数毫无关系;
2.它们都遵循C调用约定(C Calling Conversion),后面的参数先入栈,并由调用者(caller)清理堆栈。
3.参数是通过堆栈传递的,汇编里参数由编程者入栈,C里参数由系统配栈,编程者只需将参数放到函数参数表中。而当函数返回时,汇编里通过add esp+X 来清理堆栈,而在C里面,由于是系统配栈,当然系统也会自动清栈,对编程者来说,清理堆栈这一步就省了。
好,继续学习…………
阅读(703) | 评论(0) | 转发(0) |