我如何要求汇编程序"give me a full size register"?

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

我试图让汇编程序给我一个它选择的寄存器,然后使用该寄存器进行内联汇编。我正在使用下面的程序及其段错误。该程序是用 g++ -O1 -g2 -m64 wipe.cpp -o wipe.exe.

编译的

当我查看 lldb 下的崩溃时,我相信我得到的是 32 位寄存器而不是 64 位寄存器。我正在尝试使用 lea 计算地址(基数 + 偏移量),并将结果存储在汇编程序选择的寄存器中:

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

以上,我想说的是"use a register, and I'll refer to it as %2"。

当我执行反汇编时,我看到:

   0x100000b29:  leal   (%rbx,%rsi), %edi
-> 0x100000b2c:  movb   [=12=]x0, (%edi)

因此生成的代码似乎使用 64 位值(rbx 和 rsi)计算和寻址,但将其保存到 32 位寄存器 (edi)(汇编器选择)。

以下是崩溃时的值:

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

下面是关于输入操作数的快速说明。如果我删除 "r" (2),那么在调用 lea 时引用 %2 时会出现编译器错误:invalid operand number in inline asm string.

如何告诉汇编器 "give me a full size register" 然后在我的程序中引用它?


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\n"        /* 0-based index */
         "lea (%0, %1), %2\n"   /* calcualte ptr[idx] */
         "movb [=14=], (%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;
}

对于这些内联汇编问题,我们深表歉意。我希望这是最后一次。由于这样的痛点(以及我的记忆力衰退),我对在 GCC 中使用内联汇编并不感到兴奋。但鉴于 GCC 对 C 中限定符 volatile 的解释,这是我知道的唯一 合法 方法。

如果有兴趣,GCC 会将 C 的 volatile 限定符解释为 hardware backed memory,其他任何内容都是滥用,它会导致非法程序。所以下面的对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;

有趣的是,Microsoft 使用了更习惯的解释 volatile(大多数程序员所期望的 - 即任何东西都可以改变内存,而不仅仅是内存映射硬件),上面的代码是可以接受的。

gcc 内联 asm 是一个复杂的野兽。 "r" (2) 表示分配一个 int 大小的寄存器并用值 2 加载它。如果您只需要一个任意的临时寄存器,您可以声明一个 64 位早期破坏虚拟输出,例如输出部分中的 "=&r" (dummy),之前声明了 void *dummy。您可以参考gcc manual了解更多详情。

至于最终的代码片段,您似乎想要一个内存屏障,正如链接电子邮件中所说的那样。见 manual for example.