聲明:轉載請註明原連接http://my.oschina.net/u/1167407/blog/484426編程
今天在看《Professional Assembly Language》一書的第四章的Using C Library Functions in Assembly一節時,因爲我使用的是64位的Linux系統,因此遇到了一些問題,其中有些挺有用的信息。因此,記錄下來以避免遺忘。bash
Using C Library Functions in Assembly這一小節介紹瞭如何在彙編程序中調用C的庫函數。書中給出的示例代碼以下:架構
.section .data output: .asciz 「The processor Vendor ID is ‘%s’\n」 .section .bss .lcomm buffer, 12 .section .text .globl _start _start: movl $0, %eax cpuid movl $buffer, %edi movl %ebx, (%edi) movl %edx, 4(%edi) movl %ecx, 8(%edi) pushl $buffer pushl $output call printf addl $8, %esp pushl $0 call exit
接下來使用gnu as彙編這段程序ide
提示push的後綴有問題。其緣由是彙編器將其當作了64位代碼處理,在源程序的開頭加入.code32語句。再次彙編,結果以下圖,成功。函數
而後進行將彙編生成的目標文件和C庫進行動態連接。運行程序,發現問題。工具
因而,我就想幹脆就直接將寄存器都改成64位的算了,估計就能夠了。說幹就幹,修改源代碼以下:ui
.section .data output: .asciz "The processor Vendor ID is ‘%s’\n" .section .bss .lcomm buffer, 12 .section .text .globl _start _start: movq $0, %rax cpuid movq $buffer, %rdi movq %rbx, (%rdi) movq %rdx, 4(%rdi) movq %rcx, 8(%rdi) pushq $buffer pushq $output call printf addq $16, %rsp pushq $0 call exit
再次彙編、連接和運行。spa
這裏咱們能夠看到一個奇怪的現象,字符串The processor Vendor ID is並無輸出,而只是輸出了buffer中的內容。.net
因而咱們寫一個以下的C語言程序,來對比一下,查找緣由。code
#include <stdio.h> int a; int main(){ a=3222; printf("%d\n",a); return 0; }
用GCC編譯出彙編代碼,以下
.file "helloworld.c" .comm a,4,4 .section .rodata .LC0: .string "%d\n" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl $3222, a(%rip) movl a(%rip), %eax movl %eax, %esi movl $.LC0, %edi movl $0, %eax call printf movl $0, %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (Gentoo 4.8.4 p1.6, pie-0.6.1) 4.8.4" .section .note.GNU-stack,"",@progbits
運行程序以下圖,能夠看出程序彷佛並無使用棧來傳遞參數,並且程序運行正常。
查找資料得知因爲x64位架構寄存器增多,GCC(其餘編譯器暫時尚未試驗)開始默認使用相似於_fastcall的調用約定。約定以下:
rdi,rsi,rdx,rcx,r8,r9 用做函數參數,依次對應第1個參數,第2個參數……,第6個參數,多於6個參數時,多的部分從右往左依次壓入棧中
接下來編寫一個傳遞更多參數的程序驗證以上說法
#include <stdio.h> int a,b,c,d,e,f,g; int main(){ a=1111; b=2222; c=3333; d=4444; e=5555; f=6666; g=7777; printf("%d %d %d %d %d %d %d\n",a,b,c,d,e,f,g); return 0; }
其對應的彙編文件以下
.file "helloworld.c" .comm a,4,4 .comm b,4,4 .comm c,4,4 .comm d,4,4 .comm e,4,4 .comm f,4,4 .comm g,4,4 .section .rodata .LC0: .string "%d %d %d %d %d %d %d\n" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp movl $1111, a(%rip) movl $2222, b(%rip) movl $3333, c(%rip) movl $4444, d(%rip) movl $5555, e(%rip) movl $6666, f(%rip) movl $7777, g(%rip) movl g(%rip), %edi movl f(%rip), %esi movl e(%rip), %r9d movl d(%rip), %r8d movl c(%rip), %ecx movl b(%rip), %edx movl a(%rip), %eax movl %edi, 8(%rsp) movl %esi, (%rsp) movl %eax, %esi movl $.LC0, %edi movl $0, %eax call printf movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (Gentoo 4.8.4 p1.6, pie-0.6.1) 4.8.4" .section .note.GNU-stack,"",@progbits
能夠看到除了g和f被壓到了棧上,其他參數是經過edi,esi,edx,ecx,r8d,r9d這6個寄存器(都是32位的)傳入的。
接下來回到最初的問題中,咱們能夠用以上方式將參數傳遞給printf函數解決以前的問題。
但我仍然不死心,想要將書上的代碼直接成功運行。因而,用readelf工具查看添加了.code32代碼的源文件彙編後獲得的目標文件。
注意到Class: ELF64這一行,儘管加上了.code32,彙編器仍然將其做爲64位代碼進行彙編,而咱們卻將這個目標文件和32位C庫進行連接,因而出現了兩部分不匹配的現象,因此運行可執行文件時會提示bash: ./test32: Accessing a corrupted shared library的錯誤。
接下來就是要想辦法讓gnu as彙編出ELF32格式的目標文件。查命令手冊知道用選項--32便可。此次我直接使用書上給的代碼(不加.code32),彙編連接,結果以下:
此次連接時出錯了,繼續查找資料知道了經過添加-m elf_i386選項指定架構後能夠解決這個問題。繼續試驗
這下連接運行都沒有問題了。問題解決!