使用GCC内联ARM asm分支到地址

时间:2015-05-20 01:36:56

标签: gcc assembly arm inline-assembly

我想使用 <Button android:id="@+id/apply" style="?android:attr/borderlessButtonStyle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/md_cyan_A700" android:text="Cancel"/> branch到特定地址(不是标签),而不修改ARM assembly寄存器。所以我选择LR代替BBL。 我希望在BX中完成此操作。

Here是文档,这是我尝试过的:

GCC inline asm

这是一个C宏,可以用#define JMP(addr) \ __asm__("b %0" \ : /*output*/ \ : /*input*/ \ "r" (addr) \ ); 调用。当我运行它时,我收到以下错误:

address

错误是因为error: undefined reference to 'r3'的使用。我调查了一下,我发现它可能是gcc 4.9。*版本的错误。

BTW,我在"r"上使用Android/Linux Gcc 4.9 cross compiler。 另外,我不知道我应该在OSX装载什么东西。

干杯!

修改 我将宏更改为此,我仍然得到Rm

undefined reference to r3 and r4

说明: 将变量的地址加载到r5,然后将该地址的值加载到r4。然后将1加到LSB(ARM规范要求的emm?)。最后转到那个地址。

2 个答案:

答案 0 :(得分:4)

您无法分支到寄存器,只能分支到标签。如果要跳转到寄存器中的地址,则需要将其移入PC寄存器(r15)。

#define JMP(addr) \
    __asm__("mov pc,%0" \
            : /*output*/ \
            : /*input*/ \
            "r" (addr) \
           );

答案 1 :(得分:4)

由于您使用C编程,您可以使用简单的C方法而不使用任何程序集:只需将指向您要跳转的地址的指针转换为函数指针并调用它程:

((void (*)(void)) addr)();

只是对这个括号丛林的解释: 使用此代码,您将addr转换为指针(由星号(*)表示)到不带参数的函数(第二个void表示没有参数)并且还返回没事(第一void)。最后两个括号是该函数的实际调用。 Google for&#34; C函数指针&#34;有关该方法的更多信息。

但是,如果这对你不起作用,而你仍然想要采用汇编方法,那么你正在寻找的指令实际上是BX(不知道为什么你最初将它排除在外。但是我可以猜到名称&#34;分支和交换&#34;误导你相信寄存器参数与程序计数器交换(从而改变),这是 NOT 的情况,但它在开始时也让我感到困惑。)

为此,只需简单回顾一下说明:

  • B会将标签作为参数。实际上,跳转将被编码为当前位置的偏移量,这会告诉处理器向前或向后跳转许多指令(通常编译器,汇编器或链接器将负责为您计算该偏移量)。在执行期间,控制流将简单地转移到的位置,而不用更改任何寄存器(这意味着链接寄存器LR也将保持不变)。
  • BX R0将从寄存器中获取绝对(因此偏移量)地址,在本例中为R0,并继续在该地址执行。这也是在没有更改任何其他寄存器的情况下完成的。
  • BLBLX R0是前两条指令的对应部分。他们将在控制流方面做同样的事情,但最重要的是保存链接寄存器LR中的当前程序计数器。如果被调用的函数应该稍后返回,则需要这样做。

所以从本质上讲,你需要做的是:

asm("BX %0" : : "r"(addr));

指示编译器确保变量addr位于寄存器(r)中,您承诺只读取而不更改。最重要的是,在返回时你不会改变(破坏)任何其他注册。

见这里 https://gcc.gnu.org/onlinedocs/gcc/Constraints.html 有关内联汇编约束的更多信息。

为了帮助您理解为什么还有其他解决方案,这里有一些关于ARM架构的内容:

  • 程序计数器PC用于许多可作为常规寄存器R15访问的指令。它只是该确切寄存器号的别名。
  • 这意味着几乎所有算术和寄存器更改指令都可以将其作为参数。但是,对于他们中的许多人来说,它被高度弃用。
  • 如果您正在查看编译为ARM代码的程序的反汇编,则任何函数都将以以下三种方式之一结束:
    • BX LR完全符合您的要求:获取链接寄存器的内容(LRR14的别名)并跳转到该位置,有效地返回到呼叫者
    • POP {R4-R11, PC}恢复调用者保存的寄存器并跳回调用者。这几乎肯定会在函数开头有PUSH {R4-R11, LR}的对应部分:你正在将链接寄存器的内容(返回地址)推送到堆栈上,但是将它存回到程序计数器中,有效地返回给调用者结束
    • B分支到不同的函数,如果此函数以尾调用结束并将其留给该函数返回原始调用者。

希望有所帮助, 马丁