I wanted to load the address of function to a register. I have written the following inline assembly. The inline assembler returns the error "Error: undefined symbol x0 used as an immediate value". What is wrong with this code?. I am using ARM GNU toolchain on ubuntu.
void MyFunction()
{
...
}
int main()
{
__asm volatile("mov x2, %[FnAddr] \n\t"::[FnAddr]"m"(MyFunction));
}
Aim: In newly booted AArch64 system (which is in EL2), I wanted to go to EL1. For this ELR_EL2 register is to be loaded with the target address to continue the execution
There are several problems with what you have so far:
First, as Peter Cordes pointed out in comments, m
is the wrong constraint and expands to something like [x0]
.
But thinking further, materializing a function's address in a register is actually somewhat complicated. A single mov
instruction generally won't work, since the immediate is limited to 16 bits. So even if you can get the compiler to emit mov x2, #MyFunction
, your program will compile but then fail to link. Usually you would do it with an adrp/add sequence, see Understanding ARM relocation (example: str x0, [tmp, #:lo12:zbi_paddr]).
The best approach, though, is to have the compiler do it for you. By using an input operand with an r
constraint, the compiler will precede your inline asm with a sequence of adrp
, mov
or whatever is appropriate for your program's code model, get the address in some general-purpose register, and expand the operand to the name of that register (x0
,x1
, etc). You don't care here which register is chosen, because you're just going to use it as input for a msr
instruction, so you'd want to avoid hardcoding x2
anyway. (In case it did have to be x2
, you could use a local register variable to tell the compiler which register to use.)
Thus your code can simply become:
__asm volatile ("msr ELR_EL2, %[FnAddrReg]" : : [FnAddrReg] "r" (MyFunction));
Try it on godbolt (scroll down the assembly output to see the code in question).
Technically the volatile
keyword is not necessary, as an asm
with no outputs is always considered volatile
, but it doesn't hurt and might be a good hint to someone reading the code.