使用GCC内联ASM分支到地址



我想使用ARM assemblybranch转到特定地址(而不是标签),而无需修改LR寄存器。因此,我选择B而不是BLBX。我希望在GCC inline asm中完成此操作。

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

#define JMP(addr) 
    __asm__("b %0" 
            : /*output*/ 
            : /*input*/ 
            "r" (addr) 
           );

这是一个C型宏,可以用address调用。运行时,我会收到以下错误:

error: undefined reference to 'r3'

错误是由于"r"的使用情况。我对此进行了一些研究,我发现它可能是GCC 4.9的错误。*版本。

顺便说一句,我在OSX上使用Android/Linux Gcc 4.9 cross compiler。另外,我不知道我应该在Rm上加载一些东西。

欢呼!

编辑:我更改了宏,但我仍然得到undefined reference to r3 and r4

#define JMP(addr) 
    __asm__("LDR r5,=%0nt" 
            "LDR r4,[r5]nt"
            "ADD r4,#1nt" 
            "B r4" 
            : /*output*/ 
            : /*input*/ 
            "r" (addr) 
            : /*clobbered*/ 
            "r4" ,"r5" 
           );

解释:将变量的地址加载到R5,然后将该地址的值加载到R4。然后将1添加到LSB(ARM规范需要EMM?)。最后分支到该地址。

由于您在C中编程,您可以完全使用普通的C方法,而无需任何组件:只需施放可变,该变量是要寻址要跳到的指针,到函数指针并立即调用:

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

只是对这个括号丛林的解释:使用此代码,您将addr施加到指针(由Star (*)表示)到一个不采用参数的函数(第二个void表示没有参数),并且也没有返回任何内容(第一个void)。最后,最后两个括号是该功能的实际调用。Google用于" C功能指针"以获取有关该方法的更多信息。

但是,如果这对您不起作用,并且您仍然想采用汇编方法,那么您正在寻找的指令实际上是BX(不确定您最初为什么排除了这一点。但是我可以猜测命名"分支和交换"误导您相信寄存器参数已与程序计数器交换(然后更改),这是不是的情况,但在开始时也使我感到困惑)。

为此,简单地回顾说明:

  • B将以标签作为参数。实际上,跳跃将被编码为与当前位置的偏移,该位置告诉处理器将许多指令转发或向后跳跃(通常是编译器,汇编器或链接器,将为您计算该偏移)。在执行过程中,控制流将简单地转移到该位置,而无需更改任何寄存器(这意味着链接寄存器LR也将保持不变)
  • BX R0将从寄存器中获取绝对(so 不是偏移量)地址,在这种情况下为R0,并在该地址继续执行。这也可以完成,没有更改任何其他寄存器。
  • BLBLX R0是前两个指令的相应对应物。他们将做同样的事情控制流,但除此之外,除此之外,将当前程序计数器保存在链接寄存器LR中。如果应稍后返回。

本质上,您需要做的是:

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

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

请参阅此处https://gcc.gnu.org/onlinedocs/gcc/constraints.html有关内联装配约束的更多信息。

帮助您了解为什么还有其他解决方案在这里流动,这里有一些有关手臂建筑的事情:

  • 程序计数器PC用于许多指令,可作为常规寄存器R15访问。这只是该精确寄存器号码的别名。
  • 这意味着几乎所有算术和注册更改指令都可以将其作为论点。但是,对于其中许多人来说,这是高度弃用的。
  • 如果您正在查看编译为ARM代码的程序的拆卸,则任何功能都将以三件事之一结束:
    • BX LR可以做您想做的事情:获取链接寄存器的内容(LRR14的别名),然后跳到该位置,有效地返回呼叫者
    • POP {R4-R11, PC}还原呼叫者保存的寄存器并跳回呼叫者。几乎可以肯定的是,在功能开始时,这将有PUSH {R4-R11, LR}的对应物:您正在将链接寄存器的内容(返回地址)推入堆栈中,但将其存储回程序计数器中,有效地返回了呼叫者,最终
    • B分支到另一个功能,如果此功能以尾声结束并将其延长到该功能以返回原始呼叫者。

希望有帮助,马丁

您不能分支到寄存器,只能分支到标签上。如果您想跳到寄存器中的地址,则需要将其移至PC寄存器(R15)。

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

最新更新