cx86-64inline-assemblyattmov

Why are %rbp and %rax invalid operands for movl?


I'm trying to put the value of %rbp into the %rax register using __asm__ so that I can get a reference to the stack frame that won't be changed when I create an external variable.

I'm using the following code to do this:

int some_variadic_function(int n, ...)
{
    __asm__("movl %rax, %rbp");
    return 0;   
}


int main() {
    some_variadic_function(3, 1, 2, 3);
    return 0;
}

However, when I try and compile this with clang I get the following error:

using_rbp.c:4:13: error: invalid operand for instruction
    __asm__("movl %rax, %rbp");
            ^
<inline asm>:1:7: note: instantiated into assembly here
        movl %rax, %rbp
             ^~~~~
1 error generated.

This seems to be implying that I can't put %rbp directly into %rax. Is that's what happening, or is there another issue with this code?

For reference, I'm on Mac with x86-64 hardware.


Solution

  • You have made a whole bunch of mistakes. Let's take them one by one:

    1. In AT&T assembly syntax for x86, if you provide a size suffix on the instruction, it must match the sizes inferred from the operands. Size suffixes are almost always unnecessary for x86 (the only case I can think of, offhand, where they're needed is when the source operand is an immediate value and the destination is memory): I recommend leaving them out unless the assembler complains about their absence.

    2. In AT&T assembly syntax, the destination comes second; mov %rax, %rbp copies RAX into RBP, not the other way around.

    3. In GNU-style inline assembly, % begins an escape sequence (like in printf); to write a literal register name you have to double the percent sign. This is why you are getting an error from clang, rather than from the assembler.

    4. GNU-style inline assembly must be annotated with "operand constraints" so the compiler understands what you are doing. You cannot simply move a value into RAX and expect that to become the return value. See https://gcc.gnu.org/onlinedocs/gcc-11.3.0/gcc/Using-Assembly-Language-with-C.html -- read all of this manual section, not just the summary page I linked to.

    5. In the 64-bit System V ABI for x86, %rbp is an ordinary general-purpose register, not a "frame pointer".

    The correct way to do what you were trying to do is

    long some_variadic_function(int n, ...)
    {
        long rv;
        __asm__("mov %%rbp, %0" : "=r" (rv));
        return rv;
    }
    

    However, this must be compiled with -fno-omit-frame-pointer or the return value will be garbage.

    You are probably suffering from the infamous XY problem. Ask a new question about the thing you were originally trying to do, not about the details of your attempted solution to the problem, and we can probably be more helpful.