【版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途】
除了内联汇编以外,还有一种途径可以把汇编代码整合到C/C++语言中,C/C++语言可以直接调用汇编函数,把输入值传递给函数,然后从函数获得输出值。
如果希望汇编语言函数和C/C++程序一起工作,就必须显示地遵守C样式的函数格式,也就是说所有输入变量都必须从堆栈读取,并且大多数输入值都返回到EAX嫁寄存器中。在汇编函数代码中,C样式函数对于可以修改哪些寄存器和函数必须保留哪些寄存器有着特定的规则。如果必须保留的寄存器被修改了,那么必须恢复寄存器的原始值,否则在执行返回发出调用的C程序会出现不可预料的后果。在函数中使用通用寄存器必须谨慎。下表列出寄存器在函数中的状态:
寄存器 |
状态 |
eax |
用于保存输出值,但是可能在函数返回之前被修改 |
ebx |
用于指向全局偏移表,必须保留 |
ecx |
在函数中可用 |
edx |
在函数中可用 |
ebp |
C程序使用它作为堆栈基址指针;必须保留 |
esp |
C程序使用它作为堆栈基址指针;必须保留 |
edi |
C程序使用它作为堆栈基址指针;必须保留 |
esi |
C程序使用它作为局部寄存器;必须保留 |
ST(0) |
保存浮点输出值 |
ST(1)-ST(7) |
在函数中可用 |
被调用的函数必须保留ebx、edi、esi、esp寄存器,这要求在执行函数代码之前把寄存器的值压入堆栈,并且在函数准备返回调用程序时把他们弹出堆栈。
1 C函数调用的汇编语言函数的基本模板如下:
-
.section .text
-
.type func, @function
-
func:
-
pushl %ebp
-
movl %esp, %ebp
-
subl $12, %esp #为函数局部变量保留堆栈空间,可以保存3个4字节的数据值。在函数中,相对于ebp寄存器引用局部变量。
-
pushl %edi
-
pushl %esi
-
pushl %ebx
-
# here is function code
-
popl %ebx
-
popl %esi
-
popl %edi
-
movl %ebp, %esp
-
popl %ebp
-
ret
该模板可以用于C/C++函数使用的所有汇编语言函数。
在把C文件和包含汇编函数的S文件编译可执行文件时,需要把所有文件包含在编译器命令行中,如下:
$ gcc -o app cfile.c asmfun.s
最终编译器输出单一可执行文件app,不过这样不会生成中间文件,所以我们还可以使用单独的汇编语言目标文件编译程序。如下:
$ as -o asmfunc.o asmfunc.s
$ gcc -o app cfile.c asmfun.o
对于大型程序使用许多文件时,可以编写Makefile文件来自动完成编译连接工作。 如下:
-
# Makefile for linux as
-
CFLAGS= -Wall -g
-
ASFLAGS= -gstabs
-
SRC_BIN=target_bin
-
SRC_C=$(wildcard *.c)
-
SRC_S=$(wildcard *.s)
-
SRC_OBJ=$(SRC_C:.c=.o)
-
SRC_OBJ+=$(SRC_S:.s=.o)
-
all: $(SRC_BIN)
-
$(SRC_BIN): $(SRC_OBJ)
-
$(CC) -o $@ $(SRC_OBJ)
-
clean:
-
$(RM) $(SRC_OBJ) $(SRC_BIN) *~
-
.PHONY:
-
all clean
下面是汇编语言函数和使用该函数的C程序示例,函数接收二个整数输入参数,求它们的和,然后把结果返回到eax寄存器中:
-
# add.s
-
.type add, @function
-
.globl add
-
add:
-
pushl %ebp # 因为该函数不影响ebx,edi、esi寄存器,所以开头和结尾没有包含它们。
-
movl %esp, %ebp
-
movl 8(%ebp), %eax
-
addl 12(%ebp), %eax
-
movl %ebp, %esp
-
popl %ebp
-
ret
c文件:
-
include <stdio.h>
-
int add(int, int);
-
int main(int argc, const char *argv[])
-
{
-
int ret;
-
ret = add(7, 11);
-
printf("The return value is %d.\n", ret);
-
return 0;
-
}
make及执行结果输出如下:
-
$ make
-
cc -Wall -g -c -o main.o main.c
-
as -gstabs -o add.o add.s
-
cc -o target_bin main.o add.o
-
$ ./target_bin
-
The return value is 18.
-
$
在C函数调用之前,每个输入值都要存放在堆栈中,在使用超过一个输入值时,必须按照顺序把它们存放进堆栈,C函数后面的参数先进栈,前面的参数后进栈。由于堆栈是向下增长的,所以在汇编函数中位8(%ebp)引用第一个输入值,位置12(%ebp)引用第二个输入值。