C中汇编函数的调用(Function call to Assembly)
首先来学习一下C函数和汇编函数之间的调用关系吧。
在什么情况下才用到这种调用呢:
就是C语言的库函数中不存在的功能,例如
读写I/O;
获取CPU寄存器的相关信息.如CR0,DR0,MTRR,...
一些特殊的指令:CPUID(获取CPU的基本信息),invbin(disable缓存)...
一般大家都知道,在C语言中可以嵌套汇编,一般如下格式:
void example()
{ ...
_ASM{
push ax
push bx
......
pop bx
pop ax
}
...
}
或者是形如:
void example(){
asm push ax
asm push bx
...
asm pop bx
asm pop ax
}
这些都是很常用的,下面我要介绍的是C中直接和汇编函数的调用。
这里因为32位(IA32)和64位(X64)是有差别的,所以将他们对比来分析。
可能很多人对64位的比较陌生,这里我把32位和64位CPU的寄存器列举一下,大家自己可以做一下对比:
32位:
EAX | |
EBX | |
ECX | |
EDX | |
ESP | |
EBP | |
ESI | |
EDI | |
EFLAG | |
CS |
ES |
DS |
GS |
ES |
|
RAX | ||||
RBX | ||||
RCX | ||||
RDX | ||||
RSP | ||||
RBP | ||||
RSI | ||||
RDI | ||||
R8 | ||||
R9 | ||||
R10 | ||||
R11 | ||||
R12 | ||||
R13 | ||||
R14 | ||||
R15 | ||||
RFLAG | ||||
CS |
DS |
ES |
FS |
GS |
|
首先来看C和汇编之间的接口:
32位(IA32):
@ 所有C函数的参数都按照从后到前的顺序被压到堆栈(Stack)中
例如: void example(int a,int b,intc){...}
压栈的顺序是:先将c压入堆栈,然后是b,最后是a.
@ 汇编函数从堆栈中获取这些参数。
@ 汇编函数将结果通过EAX,EDX寄存器返回给C
64位(X64):
@ C函数中的前4个参数是通过RCX,RDX,R8,R9寄存器来传递的,如果参数的个数超过4个,则后面的参数按照IA32一样的顺序来压入堆栈,实现参数的传递。
@ 汇编函数通过RCX,RDX,R8,R9和堆栈来获取参数。
@ 汇编函数将结果通过RAX,RDX寄存器返回给C
好了,下面举个I/O读写的例子吧:
32位(IA32):
C文件:
extern void IOWrite(int Port,int Data);
extern void IORead(int Port);
void main()
{ int pData;
IOWrite(0x80,0xAA); // 向80h port写 AAh
pData=IORead(0x64); //读64h port的数据
printf("%d\n",pData); //显示从64h端口读取的数据
}
ASM文件:
.Model small,c
.586p
.data
.stack
.code
IOWrite proc near public
mov dx,[ESP+04]; 获取port
mov al,[ESP+8] ;获取data
out dx,al ;数据(data)输出到端口(port)
ret ;返回
IOWrite endp
IORead proc near public
mov dx,[ESP+04]; 获取port
in al,dx ;获取port的data
ret ;返回
IOWrite endp
end
下面的表格就是参数的压栈情况:
|
|
Data |
ESP+8 |
Port |
ESP+4 |
EIP |
ESP |
|
|
C文件:
extern void IOWrite(int Port,int Data);
extern void IORead(int Port);
void main()
{ int pData;
IOWrite(0x80,0xAA); // 向80h port写 AAh
pData=IORead(0x64); //读64h port的数据
printf("%d\n",pData); //显示从64h端口读取的数据
}
ASM文件:
.Model small,c
.586p
.data
.stack
.code
IOWrite proc near public
mov al,dl; 获取data
mov dx,cx ;获取port
out dx,al ;数据(data)输出到端口(port)
ret ;返回
IOWrite endp
IORead proc near public
mov dx,cx; 获取port
in al,dx ;获取port的data
ret ;返回
IOWrite endp
end
我们可以自己比较一下差别,就能更深刻得了解如何实现对汇编函数的调用,以及32位和64位的差别。这是最基本的一些应用而已。感觉要把自己知道的东西写出来,需要照顾到很多东西,不是一件简单的事,写博客很费时间啊,不过对自己系统的掌握知识很是很有帮助的,哈哈,继续努力!Fighting!!