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;
  1. register:C 语言的一个关键字,表示该变量建议存储在 CPU 寄存器中,而不是内存中。
  2. _gpr1:这是声明的变量名
  3. asm (GPR1):将 C 变量_gpr1与汇编寄存器 a7 绑定起来。这样编译器会将该_gpr1的值加载到a7寄存器中.
  4. = type;:这部分是变量 _gpr1 的初始化,_gpr1 被初始化为 type 的值。

这行代码包含的关键意思就是将变量_gpr1与寄存器a7连接起来。

最后

asm volatile ("ecall" : "=r" (ret) : "r"(_gpr1), "r"(_gpr2), "r"(_gpr3), "r"(_gpr4));
  1. 输出操作数(: "=r" (ret)
    • =r 是一个输出操作数约束符,告诉编译器,输出操作数 ret(存储返回值的变量)需要被存储在一个通用寄存器中。
    • ret 是一个变量,它将存储 ecall 执行后的返回值(即通过 a0 寄存器返回的值)。在执行 ecall 后,a0 寄存器中存放的值将被复制到 ret
  2. 输入操作数(: "r"(_gpr1), "r"(_gpr2), "r"(_gpr3), "r"(_gpr4)
    • "r" 表示 a7a0a1a2(也就是 RISC-V 的通用寄存器)
    • _gpr1, _gpr2, _gpr3, _gpr4 是用来传递给 ecall 的四个输入参数,分别放在寄存器a7a0a1a2

具体来说,_gpr1 会被存储到 a7 寄存器,_gpr2 会被存储到 a0 寄存器,_gpr3 会被存储到 a1 寄存器,_gpr4 会被存储到 a2 寄存器。

此时ecall执行后,ret变量将包含a0寄存器的值。

posted @ 2024-11-13 15:39  上山砍大树  阅读(7)  评论(0编辑  收藏  举报