我正在尝试允许汇编程序给我一个它选择的寄存器,然后将该寄存器与内联汇编一起使用。我正在使用下面的程序,以及它的分段错误。该程序编译为g++ -O1 -g2 -m64 wipe.cpp -o wipe.exe
。
当我看到lldb下的崩溃时,我相信我得到的是32位寄存器而不是64位寄存器。我正在尝试使用lea
计算地址(基数+偏移量),并将结果存储在汇编程序选择的寄存器中:
"lea (%0, %1), %2\n"
上面,我试图说“使用寄存器,我将其称为%2”。
当我执行反汇编时,我看到:
0x100000b29: leal (%rbx,%rsi), %edi
-> 0x100000b2c: movb $0x0, (%edi)
因此,生成的代码似乎使用64位值(rbx和rsi)计算和地址,但将其保存到32位寄存器(edi)(汇编程序选择)。
以下是崩溃时的值:
(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
下面输入操作数的快速说明。如果我删除了"r" (2)
,那么当我在%2
:lea
的调用中引用invalid operand number in inline asm string
时出现编译错误。
如何告诉汇编程序“给我一个完整规格的寄存器”,然后在我的程序中参考它?
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;
}
对于这些内联汇编问题,我们深表歉意。我希望这是最后一个。我不是很高兴在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;
有趣的是,微软使用volatile
的更习惯的解释(大多数程序员都期望 - 即,任何东西都可以改变内存,而不仅仅是内存映射硬件),上面的代码是可以接受的。
答案 0 :(得分:4)
gcc inline asm是一个复杂的野兽。 "r" (2)
表示分配int
大小的寄存器并使用值2
加载它。如果你只需要一个任意的临时寄存器,你可以声明一个64位的早期clobber虚拟输出,例如输出部分中的"=&r" (dummy)
,前面已经声明了void *dummy
。有关详细信息,请参阅gcc manual。
至于最终的代码片段看起来像是一个内存障碍,就像链接的电子邮件所说的那样。请参阅manual for example。