在彙編語言中調用C庫函數

其實在彙編語言中也是可以使用C庫函數的,這一節我們來看一下如何在彙編語言中調用C庫函數以使得我們的程序看上去很方便地和用戶交互。

C庫包括C程序通用的喝多函數,如printf和exit等,下面我們緊接著上一節的知識來實現一個兩整數想加的計算並輸出計算結果的程序。

# libc.s
.section .data
output:
    .asciz "The result is %d. \n"     # printf 函數需要空字符結尾的字符串

.section .bss
    .lcomm result, 4

.section .text
.globl _start
_start:
    nop
    movl $0, result
    movl %esp, %ebp

    pushl 8(%ebp)           # 把第一個命令行參數指針壓入堆棧
    call atoi               # 把參數字符串轉換為整型
    movl %eax, result       # atoi函數返回值返回到eax寄存器中

    pushl 12(%ebp)          # 把第二個命令行參數指針壓入堆棧
    call atoi
    addl %eax, result

    pushl result            # 把參數傳遞給函數printf,必須把他們壓入堆棧。
    pushl $output            # 參數放入順序和printf獲取的順序相反。
    call printf

    pushl $0
    call exit

直接make編譯鏈接結果:

$ make
as -gstabs  -o libc.o libc.s
ld -o arg libc.o
libc.o:libc.s:18: undefined reference to `atoi'
libc.o:libc.s:22: undefined reference to `atoi'
libc.o:libc.s:27: undefined reference to `printf'
libc.o:libc.s:30: undefined reference to `exit'
make: *** [arg] 錯誤 1

提示函數未定義,說明C庫文件沒有連接到程序目標代碼。在linux系統上,把C函數連接到彙編語言有兩種方法:靜態鏈接和動態鏈接。靜態鏈接會吧函數目標文件代碼直接鏈接到應用程序的可執行文件中,這樣可執行文件就會變得巨大,事實上一般我們自己寫的程序庫是使用靜態鏈接,而系統的庫一般使用動態鏈接。動態鏈接不把函數代碼鏈接到可執行文件程序中,在程序啟動時有操作系統調用動態鏈接庫。

在linux系統上,標準的C動態庫位於libc.so.x文件中,其中x值代表庫的版本。為了鏈接libc.so文件,必須使用GNU連接器的-l參數,鏈接方式和C語言鏈接庫的方式類似。所以在鏈接時使用如下方式連接:

ld -o $@ $(SRC_OBJ) -dynamic-linker /lib/ld-linux.so.2 -lc
修改後再編譯就成功了。
$ ./libc 1 45 The result is 46.
$ ./libc 100 23
The result is 123.
$

這樣程序就直接打印輸出結果了。相比我們之前的程序,現在的程序可以直接將結果輸出在終端,而不需要去查看程序的返回碼。


书籍推荐