提问者:小点点

了解一段用于 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:

根据这里,<code>here 1</code>应该是OutputOperands部分,这意味着

由AssemblerTemplate中的指令修改的C变量的逗号分隔列表。

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

问题2:

对于< code>HERE 2,它表示InputOperands,也就是说:

由汇编程序模板中的指令读取的 C 表达式的逗号分隔列表。允许使用空列表。请参阅输入操作数。

根据这里,SVC 指令只期望 1 个参数 imm。但是我们指定了 4 个输入操作数,如 retr1、r2、r6

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

我猜svc处理程序使用这些寄存器,所以我需要在< code>SVC指令之前准备它们。但是,如果我只是像从< code>A到< code>B那样准备它们,而不把它们作为输入操作数呢?会不会有一些误差?

问题3:

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


共1个答案

匿名用户

我猜这是一些系统调用约定。

这是没有优化的编译结果。仔细观察代码中发生的事情,可以看到在保存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 语句中将它们作为输入提及,就无法可靠地像从 A 到 B 部分一样准备它们。仅将函数参数分配给局部变量是不够的,因为没有任何内容可以强制此赋值和 asm 语句的正确排序。除非使用警告作为错误进行编译,并且对未使用但已设置的变量启用警告,否则不会有编译时错误。

问题3:最后,C1和C2的意义何在?它们似乎完全是多余的。r0 仍然存在。

是的。使用-O编译将消除这个冗余的动作以及大部分序言。