ARM 内联 asm:使用从内存中读取的值退出系统调用
ARM inline asm: exit system call with value read from memory
问题
我想在 Linux Android 设备上使用内联汇编在 ARM 中执行退出系统调用,并且我希望从内存中的某个位置读取退出值。
例子
如果不给出这个额外的参数,调用的宏如下所示:
#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());
错误
invalid 'asm': operand number out of range
我无法解释为什么会出现此错误,因为我在上述代码段 (%0/var) 中使用了一个输入变量。另外,我尝试使用常规变量,但仍然出现相同的错误。
Extended-asm syntax 需要写入 %%
才能在 asm 输出中获得单个 %
。例如对于 x86:
asm("inc %eax") // bad: undeclared clobber
asm("inc %%eax" ::: "eax"); // safe but still useless :P
%r7
将 r7
视为操作数。正如评论者所指出的,只需省略 %
s,因为 ARM 不需要它们,即使使用 GNU as
.
不幸的是,doesn't seem to be a way to request input operands in specific registers on ARM,您可以使用 x86 的方式。 (例如,"a"
约束具体表示 eax
)。
您可以使用 register int var asm ("r7")
强制 var 使用特定的寄存器,然后使用 "r"
约束并假设它将在该寄存器中。我不确定这是否总是安全的,或者是一个好主意,但即使在内联之后它似乎也能工作。 @Jeremy 评论说这项技术是 GCC 团队推荐的。
我确实生成了一些高效的代码,这避免了在 reg-reg 移动上浪费指令:
See it on the 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)
: "memory" // any side-effects on shared memory need to be done before this, not delayed until after
);
// __builtin_unreachable(); // optionally let GCC know the inline asm doesn't "return"
}
#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"
约束并在内联汇编中包含 ldr
。那么你就不需要 register int var asm("r0")
技巧来避免为该操作数浪费 mov
。
可能并不总是需要 mov r7, #1
,这也是我使用 register asm()
语法的原因。如果 gcc 想要在函数中其他地方的寄存器中使用 1
常量,它可以在 r7
中完成,因此它已经存在于 ASM_EXIT.
任何时候 GNU C 内联 asm 语句的第一条或最后一条指令是 mov
条指令,可能有一种方法可以通过更好的约束来删除它们。
问题
我想在 Linux Android 设备上使用内联汇编在 ARM 中执行退出系统调用,并且我希望从内存中的某个位置读取退出值。
例子
如果不给出这个额外的参数,调用的宏如下所示:
#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());
错误
invalid 'asm': operand number out of range
我无法解释为什么会出现此错误,因为我在上述代码段 (%0/var) 中使用了一个输入变量。另外,我尝试使用常规变量,但仍然出现相同的错误。
Extended-asm syntax 需要写入 %%
才能在 asm 输出中获得单个 %
。例如对于 x86:
asm("inc %eax") // bad: undeclared clobber
asm("inc %%eax" ::: "eax"); // safe but still useless :P
%r7
将 r7
视为操作数。正如评论者所指出的,只需省略 %
s,因为 ARM 不需要它们,即使使用 GNU as
.
不幸的是,doesn't seem to be a way to request input operands in specific registers on ARM,您可以使用 x86 的方式。 (例如,"a"
约束具体表示 eax
)。
您可以使用 register int var asm ("r7")
强制 var 使用特定的寄存器,然后使用 "r"
约束并假设它将在该寄存器中。我不确定这是否总是安全的,或者是一个好主意,但即使在内联之后它似乎也能工作。 @Jeremy 评论说这项技术是 GCC 团队推荐的。
我确实生成了一些高效的代码,这避免了在 reg-reg 移动上浪费指令:
See it on the 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)
: "memory" // any side-effects on shared memory need to be done before this, not delayed until after
);
// __builtin_unreachable(); // optionally let GCC know the inline asm doesn't "return"
}
#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"
约束并在内联汇编中包含 ldr
。那么你就不需要 register int var asm("r0")
技巧来避免为该操作数浪费 mov
。
可能并不总是需要 mov r7, #1
,这也是我使用 register asm()
语法的原因。如果 gcc 想要在函数中其他地方的寄存器中使用 1
常量,它可以在 r7
中完成,因此它已经存在于 ASM_EXIT.
任何时候 GNU C 内联 asm 语句的第一条或最后一条指令是 mov
条指令,可能有一种方法可以通过更好的约束来删除它们。