syscall
syscall
分析_syscall_
的相关参数:
intptr_t _syscall_(intptr_t type, intptr_t a0, intptr_t a1, intptr_t a2) {
register intptr_t _gpr1 asm (GPR1) = type;
register intptr_t _gpr2 asm (GPR2) = a0;
register intptr_t _gpr3 asm (GPR3) = a1;
register intptr_t _gpr4 asm (GPR4) = a2;
register intptr_t ret asm (GPRx);
asm volatile (SYSCALL : "=r" (ret) : "r"(_gpr1), "r"(_gpr2), "r"(_gpr3), "r"(_gpr4));
return ret;
}
其相关的宏定义为
// helper macros
#define _concat(x, y) x ## y
#define concat(x, y) _concat(x, y)
#define _args(n, list) concat(_arg, n) list
#define _arg0(a0, ...) a0
#define _arg1(a0, a1, ...) a1
#define _arg2(a0, a1, a2, ...) a2
#define _arg3(a0, a1, a2, a3, ...) a3
#define _arg4(a0, a1, a2, a3, a4, ...) a4
#define _arg5(a0, a1, a2, a3, a4, a5, ...) a5
// extract an argument from the macro array
#define SYSCALL _args(0, ARGS_ARRAY)
#define GPR1 _args(1, ARGS_ARRAY)
#define GPR2 _args(2, ARGS_ARRAY)
#define GPR3 _args(3, ARGS_ARRAY)
#define GPR4 _args(4, ARGS_ARRAY)
#define GPRx _args(5, ARGS_ARRAY)
# define ARGS_ARRAY ("ecall", "a7", "a0", "a1", "a2", "a0")
首先从GPR1
开始拆解宏定义。
GPR1 -> _args(1, ARGS_ARRAY)
_args(1, ARGS_ARRAY) -> _arg1 ARGS_ARRAY
_arg1(ARGS_ARRAY) -> a7
其他拆解同理,拆解完毕后分析内联汇编语法。
intptr_t _syscall_(intptr_t type, intptr_t a0, intptr_t a1, intptr_t a2) {
register intptr_t _gpr1 asm ("a7") = type;
register intptr_t _gpr2 asm ("a0") = a0;
register intptr_t _gpr3 asm ("a1") = a1;
register intptr_t _gpr4 asm ("a2") = a2;
register intptr_t ret asm ("a0");
asm volatile ("ecall" : "=r" (ret) : "r"(_gpr1), "r"(_gpr2), "r"(_gpr3), "r"(_gpr4));
return ret;
}
例如
register intptr_t _gpr1 asm ("a7") = type;
register
:C 语言的一个关键字,表示该变量建议存储在 CPU 寄存器中,而不是内存中。_gpr1
:这是声明的变量名asm (GPR1)
:将 C 变量_gpr1
与汇编寄存器a7
绑定起来。这样编译器会将该_gpr1
的值加载到a7
寄存器中.= type;
:这部分是变量_gpr1
的初始化,_gpr1
被初始化为type
的值。
这行代码包含的关键意思就是将变量_gpr1
与寄存器a7
连接起来。
最后
asm volatile ("ecall" : "=r" (ret) : "r"(_gpr1), "r"(_gpr2), "r"(_gpr3), "r"(_gpr4));
- 输出操作数(
: "=r" (ret)
)=r
是一个输出操作数约束符,告诉编译器,输出操作数ret
(存储返回值的变量)需要被存储在一个通用寄存器中。ret
是一个变量,它将存储ecall
执行后的返回值(即通过a0
寄存器返回的值)。在执行ecall
后,a0
寄存器中存放的值将被复制到ret
。
- 输入操作数(
: "r"(_gpr1), "r"(_gpr2), "r"(_gpr3), "r"(_gpr4)
)"r"
表示a7
、a0
、a1
和a2
(也就是 RISC-V 的通用寄存器)_gpr1
,_gpr2
,_gpr3
,_gpr4
是用来传递给ecall
的四个输入参数,分别放在寄存器a7
、a0
、a1
和a2
中
具体来说,_gpr1
会被存储到 a7 寄存器,_gpr2
会被存储到 a0 寄存器,_gpr3
会被存储到 a1 寄存器,_gpr4
会被存储到 a2 寄存器。
此时ecall
执行后,ret
变量将包含a0
寄存器的值。