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

问题

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

没有提供这个额外的参数,这个调用的macros看起来像:

#define ASM_EXIT() __asm__("mov %r0, #1\n\t" \ "mov %r7, #1\n\t" \ "swi #0") 

这很好。 接受一个论点,我把它调整到:

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

我用它来调用它:

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

错误

无效“asm”:操作数超出范围

我无法解释为什么我得到这个错误,因为我在上面的代码片段(%0 / var)中使用一个inputvariables。 此外,我已经尝试了一个正则variables,仍然有相同的错误。

Solutions Collecting From Web of "ARM inline asm:退出从内存中读取值的系统调用"

Extended-asm语法要求写入%%以在asm输出中获取单个% 。 例如对于x86:

 asm("inc %eax") // bad: undeclared clobber asm("inc %%eax" ::: "eax"); // safe but still useless :P 

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


不幸的是, 似乎没有办法在ARM上的特定寄存器中请求输入操作数 ,这是x86的方式。 (例如, "a"约束特别是指eax )。

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

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

在Godbolt编译器资源管理器上查看 :

 __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. 

既然你总是希望从内存中读取值,你可以使用"m"约束,并在内联asm中包含一个ldr 。 那么你将不需要register int var asm("r0")技巧来避免该操作数被浪费的mov

mov r7, #1也许并不总是需要的,这就是为什么我也使用了register asm()语法。 如果gcc需要一个函数中某个寄存器的常量,那么它可以在r7所以它已经存在于ASM_EXIT中。


任何时候,GNU C inline语句的第一条或最后一条指令都是mov指令,可能有一种方法可以通过更好的约束来删除它们。