|
3.4.3 在C程序中调用汇编函数
从C程序中调用汇编程序函数的方法与汇编程序中调用C函数的原理相同,但Linux 内核程序中不常使用。调用方法的着重点仍然是对函数参数在栈中位置的确定上。当然,如果调用的汇编语言程序比较短,那么可以直接在C程序中使用上面介绍的内联汇编语句来实现。下面举例说明编制这类程序的方法。包含两个函数的汇编程序callee.s如下。
/* 本汇编程序利用系统调用sys_write()实现显示函数int mywrite(int fd, char * buf, int count)。 函数int myadd(int a, int b, int * res) 用于执行 a+b = res运算。若函数返回0,则说明溢出。 注意:如果在现在的Linux系统(如RedHat 9)下编译,则请 去掉函数名前的下划线'_'。 */ SYSWRITE = 4 # sys_write()系统调用号。 .global _mywrite, _myadd .text _mywrite: pushl %ebp movl %esp, %ebp pushl %ebx movl 8(%ebp), %ebx # 取调用者第1个参数:文件描述符fd。 movl 12(%ebp), %ecx # 取第2个参数:缓冲区指针。 movl 16(%ebp), %edx # 取第3个参数:显示字符数。 movl $SYSWRITE,%eax # %eax中放入系统调用号4。 int $0x80 # 执行系统调用。 popl %ebx movl %ebp, %esp popl %ebp ret _myadd: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax # 取第1个参数a。 movl 12(%ebp), %edx # 取第2个参数b。 xorl %ecx, %ecx # %ecx为0表示计算溢出。 addl %eax, %edx # 执行加法运算。 jo 1f # 若溢出则跳转。 movl 16(%ebp), %eax # 取第3个参数的指针。 movl %edx, (%eax) # 把计算结果放入指针所指位置处。 incl %ecx # 没有发生溢出,于是设置无溢出返回值。 1:movl %ecx, %eax # %eax中是函数返回值。 movl %ebp, %esp popl %ebp ret |
该汇编文件中的第1个函数mywrite()利用系统中断0x80调用系统调用sys_write(int fd, char *buf, int count)实现在屏幕上显示信息。对应的系统调用功能号是4(参见include/ unistd.h),3个参数分别为文件描述符、显示缓冲区指针和显示字符数。在执行int 0x80之前,寄存器%eax中需要放入调用功能号(4),寄存器%ebx、%ecx和%edx要按调用规定分别存放fd、buf和count。函数mywrite()的调用参数个数和用途与sys_write()完全一样。
第2个函数myadd(int a, int b, int *res)执行加法运算。其中参数res是运算的结果。函数返回值用于判断是否发生溢出。如果返回值为0表示计算已发生溢出,结果不可用。否则计算结果将通过参数res返回给调用者。
注意:如果在现在的Linux系统(如RedHat 9)下编译callee.s程序,则请去掉函数名前的下画线"_"。调用这两个函数的C程序caller.c如下所示。
/* 调用汇编函数mywrite(fd, buf, count)显示信息; 调用myadd(a, b, result)执行加运算。 如果myadd()返回0,则表示加函数发生溢出。首先显示 开始计算信息,然后显示运算结果。 */ 01 int main() 02 { 03char buf[1024]; 04int a, b, res; 05char * mystr = "Calculating...\n"; 06char * emsg = "Error in adding\n"; 07 08a = 5; b = 10; 09mywrite(1, mystr, strlen(mystr)); 10if (myadd(a, b, &res)){ 11 sprintf(buf, "The result is %d\n", res); 12 mywrite(1, buf, strlen(buf)); 13} else { 14 mywrite(1, emsg, strlen(emsg)); 15} 16return 0; 17 } | 该函数首先利用汇编函数mywrite()在屏幕上显示开始计算的信息"Calculating...",然后调用加法计算汇编函数myadd()对a和b两个数进行运算,并在第3个参数res中返回计算结果。最后利用mywrite()函数把格式化的结果信息字符串显示在屏幕上。如果函数myadd()返回0,则表示加函数发生溢出,计算结果无效。这两个文件的编译和运行结果如下:
[/usr/root]# as -o callee.o callee.s [/usr/root]# gcc -o caller caller.c callee.o [/usr/root]# ./caller Calculating... The result is 15 [/usr/root]# |
|