概述

我使用AT&T的规范,在linux上完成 C和汇编的互相调用,并使用gcc编译成可执行文件。
目标:汇编函数提供输出。类似C语言的函数

void hello_world(char* value)
{
    printf(value);
}

提供给C语言调用:

int main()
{
    hello_world("hello world!\n");
}

搭建AT&T的环境

  • ubuntu16.04 或 ubuntu18.04- vscode- 文件后缀: .s- gcc 下载vscode插件GNU Assembler Language Support

C代码生成为汇编代码

提供一个hello.c

void hello_world(char* value)
{
    printf(value);
}

int main()
{
  hello_world("Hello World!\n");
}

为了达到使用C++调用汇编函数输出hello world!的目的,我们需要把hello_world函数转化为汇编语言编写,我们先将
gcc 将hello.c转化为汇编代码

$ gcc -S hello.c -o hello.s

转化之后的汇编代码:

	.file	"hello.c"
	.text
	.globl	hello_world
	.type	hello_world, @function
hello_world:
.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
	movq	%rdi, -8(%rbp)
	movq	-8(%rbp), %rax
	movq	%rax, %rdi
	movl	$0, %eax
	call	printf
	nop
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	hello_world, .-hello_world
	.section	.rodata
.LC0:
	.string	"Hello World!\n"
	.text
	.globl	main
	.type	main, @function
main:
.LFB1:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movl	$.LC0, %edi
	call	hello_world
	movl	$0, %eax
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1:
	.size	main, .-main
	.ident	"GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609"
	.section	.note.GNU-stack,"",@progbits

我们根据上面的汇编代码可以看到汇编的hello_world汇编函数。我们可以根据自己的理解重新写一份,步骤如下:

  • 在C中调用hello_world汇编函数,需要先声明:
extern hello_world(char* value);
  • 编写AT&T规范的汇编代码,文件后缀是.s

hello_world 汇编函数

hello.s

#hello.s
.globl hello_world
.type hello_world, @function

.section .text
hello_world:
    pushq   %rbp            #压栈
    movq    %rsp, %rbp      #把栈顶指针赋值给%rbp
    subq	$8, %rsp        #调整栈指针,向下移动8个字节,给局部变量留出空间  
    movq	%rdi, -8(%rbp)   #把rdi的值赋值给位于rbp-8的局部变量  rdi代表第一个参数
    movq	-8(%rbp), %rax   #把位于rbp-8的局部变量赋值给rax 
    #movq	%rax, %rdi       #rax 赋值给 rdi
    movq	$0, %rax        #以0结尾
    nop
    call	printf
    leave
    ret
  • 压栈和把栈顶指针赋值给%rbp是汇编函数的常规操作,基本上每个汇编函数都需要。- 调整栈指针,给局部变量留出空间- %rdi代表是第一个参数- 最后字符串结尾需要加上0- call printf调用printf函数打印寄存器中的字符串 hello.c
extern void hello_world(char* value);
int main()
{
  hello_world("Hello World!\n");
}

编译成可执行文件

$ gcc -o hello hello.c hello.s

运行

$ ./hello
Hello World!

x64

在x64汇编中调用函数时,以下寄存器用作参数。 尝试将它们提交到内存中,因为将来您会经常使用它们

第一个参数:RDI
第二个参数:RSI
第三个参数:RDX
第四个参数:RCX
第五个参数:R8
第六个参数:R9

--完--