S. Yang
S. Yang

Reputation: 13

Inline assembly with argument reorders code

I have a question about ARM inline assembly code. I am trying to call a function from a naked function, as shown below. It unexpectedly reorders the code.

extern void func_b();
__attribute__((naked)) static void func_a(void)
{
    asm volatile ( "push    {r0-r7, lr}" );
    asm volatile ( "bl      %0" : : "r"(func_b) );
    asm volatile ( "pop     {r0-r7, lr}\n"
                   "subs    pc, lr, #4" );
}

I got the following assembly code:

305c:   f642 20c7   movw    r0, #10951  ; 0x2ac7
3060:   b5ff        push    {r0, r1, r2, r3, r4, r5, r6, r7, lr}
3062:   f2c0 0000   movt    r0, #0
3066:   4780        bl      r0
3068:   e8bd 40ff   ldmia.w sp!, {r0, r1, r2, r3, r4, r5, r6, r7, lr}
306c:   f3de 8f04   subs    pc, lr, #4

My desired code should be:

305c:   b5ff        push    {r0, r1, r2, r3, r4, r5, r6, r7, lr}
305e:   f642 20c7   movw    r0, #10951  ; 0x2ac7
3062:   f2c0 0000   movt    r0, #0
3066:   4780        bl      r0
3068:   e8bd 40ff   ldmia.w sp!, {r0, r1, r2, r3, r4, r5, r6, r7, lr}
306c:   f3de 8f04   subs    pc, lr, #4

I have tried the asm volatile ( "" ::: "memory" ); compiler barrier but it does not work.

Upvotes: 0

Views: 455

Answers (2)

Peter Cordes
Peter Cordes

Reputation: 365312

What you're calling "re-ordering" is not actually re-ordering of the asm volatile code. All the asm code appears in source order. What's happening is that the compiler interleaves some compiler-generated instructions between them. This is what @Notlikethat pointed out. This is expected for operand setup.


The gcc manual points out that this usage of naked is unsupported:

Only basic asm statements can safely be included in __attribute__((naked)) functions (see Basic Asm). While using extended asm or a mixture of basic asm and C code may appear to work, they cannot be depended upon to work reliably and are not supported.

Basic Asm means asm statements without operands, so asm volatile ( "bl %0" : : "r"(func_b) ); is the problem.

As @artless_noise pointed out in a deleted answer, you don't need or want a function pointer in a register. You should just use asm("bl func_b");.

Using a "g" constraint (as mfro's answer suggests) will let the compiler choose a memory operand, so you end up with bl func_b. This is still technically unsupported, and pointless anyway, so don't do it.


If you want to write a naked function that takes args, look up in the ABI what registers to find them in. Don't use inline-asm operands to get function args or access globals.

Upvotes: 1

mfro
mfro

Reputation: 3335

with the "r" constraint on the funcb address, you instruct the assembler to do a "jump with link" based on a register value. There is no such instruction in the ARM instruction set, however.

If you relax the "r" constraint to "g", the code will probably be closer to what you desire.

Upvotes: 1

Related Questions