ARM内联asm:退出系统调用,从内存中读取值

Pas*_*lis 3 linux assembly arm system-calls inline-assembly

问题

我想在Linux Android设备上使用内联汇编在ARM中执行退出系统调用,我希望从内存中的某个位置读取退出值.

如果不给出这个额外的参数,调用的宏看起来像:

#define ASM_EXIT() __asm__("mov     %r0, #1\n\t" \
                           "mov     %r7, #1\n\t" \
                           "swi     #0")
Run Code Online (Sandbox Code Playgroud)

这很好用.要接受一个参数,我将其调整为:

#define ASM_EXIT(var) __asm__("mov     %r0, %0\n\t" \
                              "mov     %r7, #1\n\t" \
                              "swi     #0"          \
                              :                     \
                              : "r"(var))
Run Code Online (Sandbox Code Playgroud)

我用它来称呼它:

#define GET_STATUS() (*(int*)(some_address)) //gets an integer from an address

ASM_EXIT(GET_STATUS());
Run Code Online (Sandbox Code Playgroud)

错误

无效'asm':操作数超出范围

我无法解释为什么我会收到此错误,因为我在上面的代码段(%0/var)中使用了一个输入变量.此外,我尝试了一个常规变量,仍然有同样的错误.

Pet*_*des 5

Extended-asm语法需要写入%%%在asm输出中获取单个.例如,对于x86:

asm("inc %eax")                // bad: undeclared clobber
asm("inc %%eax" ::: "eax");    // safe but still useless :P
Run Code Online (Sandbox Code Playgroud)

%r7r7视为操作数.正如评论者指出的那样,只要省略%s,因为即使使用GNU,你也不需要它们用于ARM as.


不幸的是,似乎没有办法在ARM上的特定寄存器中请求输入操作数,就像x86一样.(例如,"a"约束意味着eax具体).

您可以使用register int var asm ("r7")强制var来使用特定寄存器,然后使用"r"约束并假设它将在该寄存器中.我不确定这总是安全的,或者是一个好主意,但它似乎在内联之后仍然有效.@Jeremy评论说这项技术是GCC团队推荐的.

我确实得到了一些有效的代码,这避免了在reg-reg移动上浪费指令:

在Godbolt Compiler Explorer上看到它:

__attribute__((noreturn)) static inline void ASM_EXIT(int status)
{
  register int status_r0 asm ("r0") = status;
  register int callno_r7 asm ("r7") = 1;
  asm volatile("swi  #0\n"
      :
      : "r" (status_r0), "r" (callno_r7)
  );
}

#define GET_STATUS() (*(int*)(some_address)) //gets an integer from an address

void foo(void) { ASM_EXIT(12); }
    push    {r7}    @            # gcc is still saving r7 before use, even though it sees the "noreturn" and doesn't generate a return
    movs    r0, #12 @ stat_r0,
    movs    r7, #1  @ callno,
    swi  #0
     # yes, it literally ends here, after the inlined noreturn

void bar(int status) { ASM_EXIT(status); }
    push    {r7}    @
    movs    r7, #1  @ callno,
    swi  #0                  # doesn't touch r0: already there as bar()'s first arg.
Run Code Online (Sandbox Code Playgroud)

由于您始终希望从内存中读取值,因此可以使用"m"约束并在内ldr联asm中包含a .那么你就不需要这个register int var asm("r0")技巧来避免浪费mov该操作数.

mov r7, #1可能不总是被需要,这就是为什么我使用的register asm()语法了这一点.如果gcc想要1在函数中的其他地方的寄存器中使用常量,那么它可以执行它,r7因此它已经存在于ASM_EXIT中.


每当GNU C内联asm语句的第一个或最后一个指令都是mov指令时,可能有一种方法可以用更好的约束来删除它们.