demo的主程序如下:(asm_binary.c)
1 | #include <stdio.h> |
add2这个函数的实现如下:(add.S)
1 | // sf op S sh imm12 rn rd |
汇编指令和二进制指令被封装到一个叫add2的函数里,这个函数接受一个输入,返回输入+2
的值。这里嵌入的二进制是立即数add这个指令的内容,这个指令把rn的值和imm12域段表示
的立即数相加后的值写入rd寄存器。
这里需要注意的有以下两点:1. C代码和汇编代码如何衔接;2. 汇编代码和二进制指令如何衔接。
这里因为是函数调用形式,所以C代码和汇编代码直接的接口是满足ARM64 call convention
ABI接口的,这里编译器会把函数的入参先放到x0上,所以add2里可以直接使用x0,它的值就是
add2的入参。
继续看第二个问题。体系架构的汇编手册一般都会支持直接插入二进制的语法,比如这里就是
用.word表示一个32bit的二进制编码,编译器和汇编器看到这样的写法,不会关心这32bit的
内容,直接把它放到最后的二进制里。寄存器如何排布,这里其实可以直接只用x0作为rn寄存器,
把x0的值再传给x10,是为了说明什么样的寄存器是可以用的,在我们这种场景下,caller
save的寄存器都可以直接使用(ARM64下,x10/x12都是caller save的寄存器)。
所谓caller save寄存器,就是程序在过程调用时由主调方保存的寄存器,因为被调方可能
使用caller save寄存器,主调方必须在发起调用前主动保存自己随后还需要使用的寄存器,
所以主调方也只要保存自己在使用的caller save寄存器就好。
还是如上的例子,如果我们在add2里再调用一个函数,而且在add2里使用了x10,就需要在
调用新函数之前保存x10的值。
gcc asm_binary.c add.S做编译,使用qemu-aarch64 a.out运行这个demo,可以看见打印的
返回值为3。