带参数重新排序代码的内联汇编

Inline assembly with argument reorders code

我有一个关于 ARM 内联汇编代码的问题。我正在尝试从裸函数调用函数,如下所示。它意外地重新排序代码。

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

我得到了以下汇编代码:

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

我想要的代码应该是:

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

我试过 asm volatile ( "" ::: "memory" );编译器障碍,但它不起作用。

通过对 funcb 地址的 "r" 约束,您指示汇编器根据寄存器值执行 "jump with link"。但是ARM指令集中没有这样的指令。

如果将 "r" 约束放宽到 "g",代码可能会更接近您的期望。

您所说的 "re-ordering" 实际上并不是 asm volatile 代码的重新排序。所有汇编代码均按源代码顺序出现。发生的事情是编译器在它们之间交错了一些编译器生成的指令。这是@Notlikethat 指出的。这是操作数设置的预期。


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 表示 asm 没有操作数的语句,所以 asm volatile ( "bl %0" : : "r"(func_b) ); 是问题所在。

正如@artless_noise 在删除的答案中指出的那样,您不需要或不需要寄存器中的函数指针。你应该只使用 asm("bl func_b");.

使用 "g" 约束(如 mfro 的回答所建议的那样)将使编译器选择一个内存操作数,因此您最终会得到 bl func_b。这在技术上仍然不受支持,而且毫无意义,所以不要这样做。


如果您想编写一个 naked 函数来获取参数,请在 ABI 中查找哪些寄存器以找到它们。不要使用内联汇编操作数来获取函数参数或访问全局变量。