2

I am trying to do the equivalent of b = ++a; with inline assembly, but I'm getting strange values in my variables after executing the code. I am using clang++ (g++ compatible) to compile inline assembly. Here is what I get so far:

#include <iostream>

using std::endl;
using std::cout;

int main()
{
    uint64_t a = 0;
    uint64_t b = 0;

    asm volatile(
    "pushq %%rbp;"
    "movq %%rsp, %%rbp;"
    "movl $0, -4(%%rbp);"
    "movl $0, -8(%%rbp);"
    "addq $1, -4(%%rbp);"
    "mov -4(%%rbp), %%rax;"
    "mov %%rax, -8(%%rbp);"
    "mov -4(%%rbp), %0;"
    "mov -8(%%rbp), %1;"
    "movq %%rbp, %%rsp;"
    "popq %%rbp"
    :"=r" (a), "=r" (b)
    :
    :"%rax", "%rbp", "%rsp"
    );
    cout << "a = " << a << ", b = " << b << endl;
    return 0;
}
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Oleg
  • 1,027
  • 1
  • 8
  • 18
  • 1
    What is _clang (GCC)_? – Thomas Sablik Nov 20 '19 at 16:15
  • Apple clang version 11.0.0 (clang-1100.0.33.12) Target: x86_64-apple-darwin18.7.0 – Oleg Nov 20 '19 at 16:17
  • `movl $0, -4(%%rbp)` only writes the low 32 bits hence the top 32 are random garbage. Also, a 64 bit integer is 8 bytes. So you want `-8(%rbp)` and `-16(%rbp)`. Your code is of course broken in other ways, such as overwriting the red zone of the containing function and doesn't make much sense anyway. – Jester Nov 20 '19 at 16:31
  • Thanks and yes, it is working with -8 and -16, but what should I change to be ok? – Oleg Nov 20 '19 at 16:58

1 Answers1

5

Your code is overly complex and it appears you might have looked at compiler generated code from elsewhere to create an answer.

What does b=++a; do? It first increments a and then assigns that value to b. In such an expression a is both used as input and output. The value of a is read, incremented by 1, saved and the result copied to b. With GCC's extended inline assembly you can treat a as an input and output operand using the + modifier on the constraint. b can be used with an output only operand using the = modifier on the constraint. The INC instruction can be used to increment a and the MOV instruction to copy that value b.

The inline assembly could simply look like:

#include <iostream>

using std::endl;
using std::cout;

int main()
{
    uint64_t a = 0;
    uint64_t b = 0;

    asm ("inc %0\n\t"
         "mov %0, %1"
         : "+r" (a), "=r" (b)
    );
    cout << "a = " << a << ", b = " << b << endl;
    return 0;
}

The output should be:

a = 1, b = 1


Note: Since there are no side effects other than the registers we told the compiler we'd modify it isn't necessary to use volatile on the asm statement.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • Thanks, yes this is correct but what is the red zone that I am modifying in my example? How to fix that? – Oleg Nov 21 '19 at 08:10
  • @oleg The [red zone](https://en.wikipedia.org/wiki/Red_zone_(computing)) is the 128 bytes immediately below the current RSP. You have to subtract 128 from RSP before using the stack and add 128 back when finished. An example is here: https://stackoverflow.com/a/37503773/3857942 – Michael Petch Nov 21 '19 at 08:23
  • You only have to worry about the red zone if you are putting data on the stack in 64-bit code in your inline assembly. The red zone doesn't exist in 32-bit Linux. In my example I don't use the stack so I don't need to concern myself with the red zone. – Michael Petch Nov 21 '19 at 08:24