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函數調用純彙編過程的源程序,不再示例。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章