How do I ask the assembler to “give me a full size register”?

筅森魡賤 提交于 2020-01-02 07:55:11

问题


I'm trying to allow the assembler to give me a register it chooses, and then use that register with inline assembly. I'm working with the program below, and its seg faulting. The program was compiled with g++ -O1 -g2 -m64 wipe.cpp -o wipe.exe.

When I look at the crash under lldb, I believe I'm getting a 32-bit register rather than a 64-bit register. I'm trying to compute an address (base + offset) using lea, and store the result in a register the assembler chooses:

"lea (%0, %1), %2\n"

Above, I'm trying to say "use a register, and I'll refer to it as %2".

When I perform a disassembly, I see:

   0x100000b29:  leal   (%rbx,%rsi), %edi
-> 0x100000b2c:  movb   $0x0, (%edi)

So it appears the code being generated calculates and address using 64-bit values (rbx and rsi), but saves it to a 32-bit register (edi) (that the assembler chose).

Here are the values at the time of the crash:

(lldb) type format add --format hex register
(lldb) p $edi
(unsigned int) $3 = 1063330
(lldb) p $rbx
(unsigned long) $4 = 4296030616
(lldb) p $rsi
(unsigned long) $5 = 10

A quick note on the Input Operands below. If I drop the "r" (2), then I get a compiler error when I refer to %2 in the call to lea: invalid operand number in inline asm string.

How do I tell the assembler to "give me a full size register" and then refer to it in my program?


int main(int argc, char* argv[])
{
    string s("Hello world");
    cout << s << endl;

    char* ptr = &s[0];
    size_t size = s.length();

    if(ptr && size)
    {
        __asm__ __volatile__
        (
         "%=:\n"                /* generate a unique label for TOP */

         "subq $1, %1\n"        /* 0-based index */
         "lea (%0, %1), %2\n"   /* calcualte ptr[idx] */
         "movb $0, (%2)\n"      /* 0 -> ptr[size - 1] .. ptr[0] */
         "jnz %=b\n"            /* Back to TOP if non-zero */

         : /* no output */
         :  "r" (ptr), "r" (size), "r" (2)
         : "0", "1", "2", "cc"
         );
    }

    return 0;
}

Sorry about these inline assembly questions. I hope this is the last one. I'm not really thrilled with using inline assembly in GCC because of pain points like this (and my fading memory). But its the only legal way I know to do what I want to do given GCC's interpretation of the qualifier volatile in C.

If interested, GCC interprets C's volatile qualifier as hardware backed memory, and anything else is an abuse and it results in an illegal program. So the following is not legal for GCC:

volatile void* g_tame_the_optimizer = NULL;
...

unsigned char* ptr = ...
size_t size = ...;

for(size_t i = 0; i < size; i++)
   ptr[i] = 0x00;

g_tame_the_optimizer = ptr;

Interestingly, Microsoft uses a more customary interpretation of volatile (what most programmers expect - namely, anything can change the memory, and not just memory mapped hardware), and the code above is acceptable.


回答1:


gcc inline asm is a complicated beast. "r" (2) means allocate an int sized register and load it with the value 2. If you just need an arbitrary scratch register you can declare a 64 bit early-clobber dummy output, such as "=&r" (dummy) in the output section, with void *dummy declared earlier. You can consult the gcc manual for more details.

As to the final code snippet looks like you want a memory barrier, just as the linked email says. See the manual for example.



来源:https://stackoverflow.com/questions/27891936/how-do-i-ask-the-assembler-to-give-me-a-full-size-register

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!