X86_64平臺下32位彙編語言調用C庫函數程序的彙編與連接

 聲明:轉載請註明原連接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選項指定架構後能夠解決這個問題。繼續試驗

這下連接運行都沒有問題了。問題解決!

相關文章
相關標籤/搜索