了解arm gcc的一段组装模板代码



下面的代码包含一些内联汇编模板:

static inline uintptr_t arch_syscall_invoke3(uintptr_t arg1, uintptr_t arg2,
uintptr_t arg3,
uintptr_t call_id)
{
register uint32_t ret __asm__("r0") = arg1;
register uint32_t r1 __asm__("r1") = arg2;
register uint32_t r2 __asm__("r2") = arg3;
register uint32_t r6 __asm__("r6") = call_id;
__asm__ volatile("svc %[svid]n"
: "=r"(ret), "=r"(r1), "=r"(r2)  <===================== HERE 1
: [svid] "i" (_SVC_CALL_SYSTEM_CALL), <===================== HERE 2
"r" (ret), "r" (r1), "r" (r2), "r" (r6)
: "r8", "memory", "r3", "ip");
return ret;
}

我和https://godbolt.org/z/znMeEMrEz像这样:

push    {r6, r7, r8}   ------------- A ------------- 
sub     sp, sp, #20
add     r7, sp, #0
str     r0, [r7, #12]
str     r1, [r7, #8]
str     r2, [r7, #4]
str     r3, [r7]
ldr     r0, [r7, #12]
ldr     r1, [r7, #8]
ldr     r2, [r7, #4]
ldr     r6, [r7]
svc #3  ------------- B -------------
mov     r3, r0  ------------- C1 -------------
mov     r0, r3  ------------- C2 -------------
adds    r7, r7, #20
mov     sp, r7
pop     {r6, r7, r8}
bx      lr

AB,汇编代码只是确保输入参数存在于目标寄存器中。我想这是某种系统调用约定。

我不明白HERE 1HERE 2的目的。

问题1:

根据这里,HERE 1应该是OutputOperands部分,这意味着

由汇编模板中的指令修改的C变量的逗号分隔列表

这是否意味着特定请求的系统调用函数将修改ret/r0r1r2注册表?

问题2:

对于HERE 2,它意味着InputOperands,这意味着:

汇编模板中的指令读取的C表达式的逗号分隔列表。允许使用空列表。请参见InputOperands

根据此处,SVC指令只需要1个参数imm。但我们指定了4个输入操作数,如retr1r2r6

为什么我们需要指定这么多呢?

我想这些寄存器是svc处理程序使用的,所以我需要在SVC指令之前准备它们。但是,如果我只是像从AB那样准备它们,并且不将它们作为输入操作数,该怎么办?会有错误吗?

问题3:

最后,C1C2有什么意义?它们看起来完全多余。r0仍然存在。

我想这是某种系统调用约定。

这是未经优化的编译结果。仔细观察代码中发生的事情,可以发现在保存r6、r7和r8之后,它所做的只是将r3移动到r6,其他一切都是多余的。

问题1:这是否意味着特定请求的系统调用函数将修改ret/r0、r1和r2注册表?

是。

问题2:根据此处,SVC指令只需要1个参数imm。但我们指定了4个输入操作数,如ret、r、r2、r6。

我们指定imm以生成正确的SVC指令,并指定其余指令以确保我们调用的系统调用将在系统调用ABI中记录的寄存器中找到其参数。

为什么我们需要指定这么多?

根据函数名,它是一个3参数的系统调用,因此我们有3个系统调用参数,显然还有系统调用标识符。

但是,如果我只是像从A到B那样准备它们,而不将它们作为输入操作数,该怎么办?会有错误吗?

如果不在asm语句中提及它们作为输入,就无法可靠地执行just prepare them like from A to B部分。仅仅将函数参数分配给局部变量是不够的,因为没有什么能强制执行此赋值和asm语句的正确顺序。除非编译时将警告作为错误,并且已为未使用但已设置的变量启用警告,否则不会出现编译时错误。

问题3:最后,C1和C2的意义是什么?它们看起来完全多余。r0仍然存在。

确实如此。使用-O进行编译将消除这种多余的移动以及大部分序言。

最新更新