linux c 中嵌入汇编

  为了提高代码的执行效率,有时程序中需要使用汇编语言来编制源代码。这就涉及在两种语言的相互调用问题,而且linux 使用的是 AT&T 的汇编语言格式,与 INTEL 汇编的有所区别,详细可以参考相关书籍。

  在汇编应用程序中调用 C 函数主要涉及汇编程序如何向 C 函数传递参数以及相关寄存器的保存,我们先看一个将C 程序转换编译成汇编程序的代码,看看汇编程序调用函数时所做的处理:

/* 交换 a 和 b 的值 */
void swap(int *a, int *b, int d, int e, int f, int g, int h, int m, int n)
{
    int c;
    c = *a; *a = *b; *b = c;
}

int main(void)
{
    int a, b;
    a = 16; b = 32;
    swap(&a, &b, 0, 0, 0, 0, 0, 0, 0);
    return (a - b);
}
 
  将文件保存为 swap.c,这里将 swap.c 文件中加入了多余的几个参数,来说明汇编程序的传递参数机制。
  使用命令 gcc -Wall -S -o swap.s swap.c 生成该 C 语言程序的汇编程序 swap.s

swap:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $16, %esp
        movl    8(%ebp), %eax
        movl    (%eax), %eax
        movl    %eax, -4(%ebp)
        movl    12(%ebp), %eax
        movl    (%eax), %edx
        movl    8(%ebp), %eax
        movl    %edx, (%eax)
        movl    12(%ebp), %edx
        movl    -4(%ebp), %eax
        movl    %eax, (%edx)
        leave
        ret
main:
        leal    4(%esp), %ecx
        andl    $-16, %esp
        pushl   -4(%ecx)
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %ecx
        subl    $52, %esp
        movl    $16, -8(%ebp)
        movl    $32, -12(%ebp)
        movl    $0, 32(%esp)
        movl    $0, 28(%esp)
        movl    $0, 24(%esp)
        movl    $0, 20(%esp)
        movl    $0, 16(%esp)
        movl    $0, 12(%esp)
        movl    $0, 8(%esp)
        leal    -12(%ebp), %eax
        movl    %eax, 4(%esp)
        leal    -8(%ebp), %eax
        movl    %eax, (%esp)
        call    swap
        movl    -8(%ebp), %edx
        movl    -12(%ebp), %eax
        movl    %edx, %ecx
        subl    %eax, %ecx
        movl    %ecx, %eax
        addl    $52, %esp
        popl    %ecx
        popl    %ebp
        leal    -4(%ecx), %esp
        ret
  在 swap 中可以看出汇编程序从 main 的栈中取出参数 a 和 b,然后进行交换,并返回;而在 main 程序中,调用 swap 之前为它的参数分配了 52 字节的空间,显然我们这里只是使用了9个参数需要的空间为 4*9=36 字节的空间就够使用,那么额外的 52-36=16 字节用作什么用途呢?另外在 main 的开始处的 andl 指令我觉得应该是使栈空间16字节对齐的操作;当将 C 程序中 swap 函数的参数改变时,可以看出其对应的汇编程序中也总是有16字节的额外空间,留作疑问。

  C 语言中嵌入汇编的操作,只需要遵循一定的格式,然后使用汇编指令就可以,如下面代码

char* strcpy(char *dest, const char *src)
{
    __asm__
    ("cld/n"
    "1:/tloadsb/n/t"
    "stosb/n/t"
    "testb %%al, %%al/n/t"
    "jne 1b"
    ::"S"(src),"D"(dest)//:"si","di","ax"/* 可能是应用程序中不用指出变化的寄存器 */
/* 否则将 gcc 报错: can't find a register in class ‘SIREG’ while reloading ‘asm’ */
    );
return dest;
}

  上面的/n/t转换是为了汇编代码整齐,没有特殊的含义。可以看出,嵌入汇编时需要使用 __asm__(); 嵌入汇编代码,也可以使用 asm(); 但是为了向前兼容最好使用前面的格式。
  而在C程序调用汇编函数过程中,只要遵循参数的反向压入栈,而且汇编程序按照依次取出参数就可以了,从上面的几个例子中不难写出一个C函数调用纯汇编过程的源程序,不再示例。

你可能感兴趣的:(linux c 中嵌入汇编)