1. 程式人生 > >C調用匯編

C調用匯編

例子:在C的main函式中調用匯編語言寫的addone函式,把傳入的引數加一然後返回。

彙編裡面首先要把用到的暫存器壓棧,使用指令global使函式可以在其他檔案中呼叫,順便說一句,C中的static函式之所以只能在本檔案中可用,就是編譯後的彙編檔案沒有用global指令,彙編的返回值放在EAX暫存器中。

彙編檔名是addone.asm,彙編方法在後面,內容如下:

global addone			;
addone:	MOV RAX, [RSP + 20]	; stack top + 20 bytes
	ADD RAX, 1
	RET

傳入的引數在棧頂以下20個位元組處,為什麼是20我也不知道,是在C的編譯後的彙編中看出來的,C檔案檔名calladdone.c,內容如下:

#include <stdio.h>

int main(){
  int a = 3;
  // printf("before call, a is %d.\n", a);
  a = addone(a);
  //printf("after call, a is %d.\n", a);
  return 0;
}

編譯後的C檔案如下:

	.file	"calladdone.c"
	.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	$3, -4(%rbp)
	movl	-4(%rbp), %eax
	movl	%eax, %edi
	movl	$0, %eax
	call	addone
	movl	%eax, -4(%rbp)
	movl	$0, %eax
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.ident	"GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1"
	.section	.note.GNU-stack,"",@progbits

中間兩句:

subq $16, %rsp
movl$3, -4(%rbp)

棧頂向低地址增長16位元組,又把a=3放在-4($rbp)的地方,就是棧頂向下12的地方,然後呼叫addone,就是這句:

call addone

會向棧中壓入一個返回地址,我機器64位,所以是8個位元組,現在引數a就在棧頂向下20的地方了。(PS:不知道那個16是不是gcc約定好的。。。)

然後編譯就可以了,彙編asm檔案:

nasm -felf64 addone.asm

我用的彙編器是nasm,64位機器所以彙編選項是-felf64,32位機器彙編選項是-felf,然後後面跟檔名。如果要除錯資訊可以再加上-g選項。

彙編後生成一個addone.o檔案,就是object file了。然後把這個檔案和C檔案一起編譯:

gcc calladdone.c addone.o

gcc沒有-o選項的話預設生成a.out檔案,gdb中執行那個檔案,加個斷點可以看到a的值確實加了1。把上面C檔案中註釋去掉,輸出如下:

before call, a is 3.
after call, a is 4.

一切正常。

最後貼一下gdb中怎麼使用x檢視暫存器和棧,在gdb中直接help x就可以看到了:

Examine memory: x/FMT ADDRESS.
ADDRESS is an expression for the memory address to examine.
FMT is a repeat count followed by a format letter and a size letter.
Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),
  t(binary), f(float), a(address), i(instruction), c(char) and s(string).
Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes).
The specified number of objects of the specified size are printed
according to the format.


Defaults for format and size letters are those previously used.
Default count is 1.  Default address is following last thing printed
with this command or "print".

比如檢視棧頂向下20個字(word)可以這麼用:x/20w $rsp

        檢視棧頂向下10個位元組(byte)可以這麼用:x/10b $rsp