多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
[TOC] ## 概述 我使用AT&T的规范,在linux上完成 C和汇编的互相调用,并使用gcc编译成可执行文件。 目标:汇编函数提供输出。类似C语言的函数 ```C void hello_world(char* value) { printf(value); } ``` 提供给C语言调用: ```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 ```asm #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 ```C extern void hello_world(char* value); int main() { hello_world("Hello World!\n"); } ``` 编译成可执行文件 ```shell $ gcc -o hello hello.c hello.s ``` 运行 ``` $ ./hello Hello World! ``` ## x64 在x64汇编中调用函数时,以下寄存器用作参数。 尝试将它们提交到内存中,因为将来您会经常使用它们 ``` 第一个参数:RDI 第二个参数:RSI 第三个参数:RDX 第四个参数:RCX 第五个参数:R8 第六个参数:R9 ```