gcc ARM内嵌组件:将函数地址移到寄存器

v7pvogib  于 2022-11-13  发布在  其他
关注(0)|答案(1)|浏览(168)

我想把函数的地址加载到寄存器中。我写了下面的内联汇编程序。内联汇编程序返回错误“Error:未定义的符号x0用作立即数”。这段代码有什么问题?。我在ubuntu上使用的是ARM GNU工具链。

void MyFunction()
{
  ...
}

int main()
{
   __asm volatile("mov x2, %[FnAddr] \n\t"::[FnAddr]"m"(MyFunction));
}

目标:在新启动的AArch64系统(在EL2中)中,我想转到EL1。因为此ELR_EL2寄存器将加载目标地址以继续执行

cu6pst1q

cu6pst1q1#

到目前为止,您所拥有的产品存在以下几个问题:
首先,正如Peter Cordes在评论中指出的,m是错误的约束,它扩展为类似[x0]的东西。
但是进一步考虑,在寄存器中具体化函数的地址实际上有些复杂。单个mov指令通常不起作用,因为立即数被限制在16位。因此,即使你能让编译器发出mov x2, #MyFunction,你的程序也会编译,但随后无法链接。通常你会用adrp/add序列来完成,请参阅了解ARM重定位(示例:字符串x 0,[临时,#:lo 12:zbi_paddr])。
不过,最好的方法是让编译器为你做这件事。通过使用一个带有r约束的输入操作数,编译器会在内联asm之前加上一个adrpmov或任何适合你的程序代码模型的序列,获取某个通用寄存器中的地址,并将操作数扩展到该寄存器的名称(x0x1,等等)。这里你不关心选择哪个寄存器,因为你只是要用它作为msr指令的输入,所以你无论如何都要避免硬编码x2。(如果它必须是x2,你可以用一个局部寄存器变量告诉编译器使用哪个寄存器。
因此,您的代码可以简单地变成:

__asm volatile ("msr ELR_EL2, %[FnAddrReg]" : : [FnAddrReg] "r" (MyFunction));

Try it on godbolt(向下滚动程序集输出以查看相关代码)。
从技术上讲,volatile关键字是不必要的,因为没有输出的asm总是被认为是volatile,但这并没有什么坏处,对阅读代码的人来说可能是一个很好的提示。

相关问题