ARM inline asm:退出系统调用并从内存中读取值 [英] ARM inline asm: exit system call with value read from memory

查看:16
本文介绍了ARM inline asm:退出系统调用并从内存中读取值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题

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

示例

如果不提供这个额外的参数,调用的宏如下所示:

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

这很好用.为了接受一个论点,我将其调整为:

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

我称之为:

#define GET_STATUS() (*(int*)(some_address))//从地址中获取整数ASM_EXIT(GET_STATUS());

错误

<块引用>

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

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

解决方案

扩展 asm 语法 需要编写 %% 以在 asm 输出中获得单个 %.例如对于 x86:

asm("inc %eax")//错误:未声明的clobberasm("inc %%eax" ::: "eax");//安全但仍然没用 :P

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

<小时>

不幸的是,那里 似乎不是一种请求输入的方法ARM 上特定寄存器中的操作数,就像 x86 一样.(例如 "a" 约束意味着 eax 专门).

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

我确实生成了一些高效的代码,这避免了在 reg-reg move 上浪费指令:

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

__attribute__((noreturn)) 静态内联 void ASM_EXIT(int status){寄存器 int status_r0 asm ("r0") = status;注册 int callno_r7 asm ("r7") = 1;asm volatile("swi #0
":: "r" (status_r0), "r" (callno_r7));}#define GET_STATUS() (*(int*)(some_address))//从一个地址获取一个整数void foo(void) { ASM_EXIT(12);}push {r7} @ # gcc 在使用前仍在保存 r7,即使它看到noreturn"并且没有生成返回movs r0, #12 @ stat_r0,movs r7, #1 @ callno,sw#0# 是的,它从字面上结束,在内联 noreturn 之后无效栏(int状态){ASM_EXIT(状态);}推 {r7} @movs r7, #1 @ callno,swi #0 # doesn't touch r0: 已经作为 bar() 的第一个参数.

<小时>

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

mov r7, #1 可能并不总是需要,这也是我使用 register asm() 语法的原因.如果 gcc 想要一个 1 常量在函数的其他地方的寄存器中,它可以在 r7 中完成,因此它已经存在于 ASM_EXIT 中.

<小时>

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

Problem

I want to execute the exit system call in ARM using inline assembly on a Linux Android device, and I want the exit value to be read from a location in memory.

Example

Without giving this extra argument, a macro for the call looks like:

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

This works well. To accept an argument, I adjust it to:

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

and I call it using:

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

ASM_EXIT(GET_STATUS());

Error

invalid 'asm': operand number out of range

I can't explain why I get this error, as I use one input variable in the above snippet (%0/var). Also, I have tried with a regular variable, and still got the same error.

解决方案

Extended-asm syntax requires writing %% to get a single % in the asm output. e.g. for x86:

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

%r7 is treating r7 as an operand number. As commenters have pointed out, just omit the %s, because you don't need them for ARM, even with GNU as.


Unfortunately, there doesn't seem to be a way to request input operands in specific registers on ARM, the way you can for x86. (e.g. "a" constraint means eax specifically).

You can use register int var asm ("r7") to force a var to use a specific register, and then use an "r" constraint and assume it will be in that register. I'm not sure this is always safe, or a good idea, but it appears to work even after inlining. @Jeremy comments that this technique was recommended by the GCC team.

I did get some efficient code generated, which avoids wasting an instruction on a reg-reg move:

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
"
      :
      : "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.


Since you always want the value read from memory, you could use an "m" constraint and include a ldr in your inline asm. Then you wouldn't need the register int var asm("r0") trick to avoid a wasted mov for that operand.

The mov r7, #1 might not always be needed either, which is why I used the register asm() syntax for it, too. If gcc wants a 1 constant in a register somewhere else in a function, it can do it in r7 so it's already there for the ASM_EXIT.


Any time the first or last instructions of a GNU C inline asm statement are mov instructions, there's probably a way to remove them with better constraints.

这篇关于ARM inline asm:退出系统调用并从内存中读取值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆