0

Below code contains some inline assembly template:

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;
}

And I got the final assembly with https://godbolt.org/z/znMeEMrEz like this:

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

From A to B, the assembly code just ensure the input arguments are present in the targeted registers. I guess this is some system call convention.

I don't understand the purpose of HERE 1 and HERE 2.

Question 1:

According to here, HERE 1 should be the OutputOperands part, which means

A comma-separated list of the C variables modified by the instructions in the AssemblerTemplate.

Does this mean the specific requested system call function will modify the ret/r0, r1 and r2regitser?

Question 2:

For HERE 2, it means InputOperands, which means:

A comma-separated list of C expressions read by the instructions in the AssemblerTemplate. An empty list is permitted. See InputOperands.

According to here, the SVC instruction expects only 1 argument imm. But we specify 4 input operands like ret, r1, r2, r6.

Why do we need to specify so many of them?

I guess these registers are used by svc handler so I need to prepare them before the SVC instruction. But what if I just prepare them like from A to B and do not mention them as the input operands? Will there be some error?

Question 3:

And at last, what's the point of the C1 and C2? They seem totally redundant. The r0 is still there.

smwikipedia
  • 61,609
  • 92
  • 309
  • 482
  • If you have the code on Godbolt, why not link it in your question? Like https://godbolt.org/z/fsbsxYx1E where I enabled optimization. See [Why does clang produce inefficient asm with -O0 (for this simple floating point sum)?](https://stackoverflow.com/q/53366394) for more about compilers spilling and reloading everything (including incoming register args) with optimization disabled. [Redundant register values in GCC-compiled binary?](https://stackoverflow.com/q/61893664) – Peter Cordes Nov 09 '22 at 08:59
  • I was planning to add the link but didn't find how to. Now I added it. Thanks. – smwikipedia Nov 10 '22 at 00:38

1 Answers1

4

I guess this is some system call convention.

This is the result of compilation without optimizations. Looking closely at what's going on in that code one can see that after saving r6, r7 and r8 all it does is moving r3 to r6, everything else is redundant.

Question 1: Does this mean the specific requested system call function will modify the ret/r0, r1 and r2 regitser?

Yes.

Question 2: According to here, the SVC instruction expects only 1 argument imm. But we specify 4 input operands like ret, r, r2, r6.

We specify imm to generate a correct SVC instruction and we specify the rest to make sure that the system call we invoke will find its arguments in the registers documented in the system call ABI.

Why do we need to specify so many of them?

According to the function name it's a 3-argument syscall, so we have 3 syscall parameters and apparently the system call identifier.

But what if I just prepare them like from A to B and do not mention them as the input operands? Will there be some error?

One cannot reliably do the just prepare them like from A to B part without mentioning them as inputs in that asm statement. Just assigning function arguments to local variables is not enough because nothing will enforce the correct ordering of this assignment and the asm statement. There will be no compile-time error unless compiling with warnings-as-errors and having enabled the warning for unused but set variables.

Question 3: And at last, what's the point of the C1 and C2? They seem totally redundant. The r0 is still there.

They are. Compiling with -O will eliminate this redundant move as well as most of the prologue.

jcmvbkbc
  • 723
  • 1
  • 4
  • 5