我如何要求汇编程序"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.
我试图让汇编程序给我一个它选择的寄存器,然后使用该寄存器进行内联汇编。我正在使用下面的程序及其段错误。该程序是用 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.