linux平台学x86汇编(十九):C语言中调用汇编函数

【版权声明:尊重原创,转载请保留出处: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 
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)引用第二个输入值。

你可能感兴趣的:(linux平台学x86汇编)