在gcc内联程序集中加载寄存器?(简单吗?)
因此,对于任何熟悉汇编的人来说,这似乎是一个非常简单的问题,但我希望有人能向我解释以下两段代码之间的区别,因为一段代码会导致分段错误,而另一段代码不会,但是(对我来说)它们看起来应该在逻辑上是等价的 工作正常:在gcc内联程序集中加载寄存器?(简单吗?),c,linux,gcc,assembly,x86,C,Linux,Gcc,Assembly,X86,因此,对于任何熟悉汇编的人来说,这似乎是一个非常简单的问题,但我希望有人能向我解释以下两段代码之间的区别,因为一段代码会导致分段错误,而另一段代码不会,但是(对我来说)它们看起来应该在逻辑上是等价的 工作正常: char *src1; int esi_out, eax; __asm__ __volatile__( "lodsb\n\t;" : "=&S" (esi_out), "=&a" (eax) : "0" (src1) ); printf(
char *src1; int esi_out, eax;
__asm__
__volatile__(
"lodsb\n\t;"
: "=&S" (esi_out), "=&a" (eax)
: "0" (src1)
);
printf("src1 %c @ %p, esi_out: %x, eax: %x\n", *src1, src1, esi_out, eax);
和打印:
src1 w @ 0x7fffce186959, esi_out: ce18695a, eax: ce186977
所以我的理解是,这段代码应该将src1的值(一个地址)加载到ESI中,将该值复制到EAX中,将ESI中的地址增加1字节,然后在退出时,将这些值输出到本地C变量ESI_out和EAX中。src1和esi_out看起来是正确的,但eax似乎关闭了。这是怎么回事
第二段代码是我们看到的一个segfault,我无法完全理解:
__asm__
__volatile__(
"movl %%ebx, %%esi\n\t;"
//"lodsb\n\t;"
: "=&S" (esi_out), "=&b" (ebx), "=&a" (eax)
: "1" (src1)
);
printf("src1 %c @ %p, esi_out: %x, eax: %x, ebx: %x\n",
*src1, src1, esi_out, eax, ebx);
注释掉lodsb命令后,它将生成:
src1 w @ 0x7ffff093b959, esi_out: f093b959, eax: f093b959, ebx: f093b959
由于lodsb命令未被注释掉,因此它会出现故障。按照我的想法,直接加载ESI值,就像上面第一种情况一样,然后加载到EBX中,然后将其移动到ESI中应该是等效的,不是吗
我错过了什么?为什么写入EAX中的值看起来不合适?我将等效程序直接写到汇编中,并使用gdb逐步完成,效果很好
如果您有任何见解,我们将不胜感激。您的第一个问题-
lodsb
正在修改AL,但不是EAX的其余部分。ce186977
的EAX值中的最后一个字节是0x77,它是十六进制,表示小写字母“w”
不幸的是,我不熟悉GCC汇编语法——当您运行代码并跨过
movl
时,哪个寄存器是目标?在我看来,EBX正在编写ESI。您的代码之前EBX中有什么?您的第一个问题-lodsb
正在修改AL,而不是EAX的其余部分。ce186977
的EAX值中的最后一个字节是0x77,它是十六进制,表示小写字母“w”
不幸的是,我不熟悉GCC汇编语法——当您运行代码并跨过
movl
时,哪个寄存器是目标?在我看来,EBX正在编写ESI。代码之前EBX中有什么?从printf中%p
的输出来看,您正在编译64位的代码,但asm代码假定为32位。试一试
__asm__
__volatile__(
"movl %%rbx, %%rsi\n\t;"
"lodsb\n\t;"
: "=&S" (esi_out), "=&b" (ebx), "=&a" (eax)
: "1" (src1)
);
printf("src1 %c @ %p, esi_out: %x, eax: %x, ebx: %x\n",
*src1, src1, esi_out, eax, ebx);
您还应该将esi\u out
和ebx
声明为指针类型(void*
或char*
)或uintptpr\u t
)
发生的情况是,lodsb在64位模式下使用RSI作为其源地址,但您只将指针值的低32位放入RSI,因此它不包含有效地址,因此存在SEGFULT。正如Slagh所说,lodsb只修改寄存器(al)的低位8位。您应该屏蔽其他位(eax&0xff),清除rax(xor%rax,%rax),或者将eax声明为字符
如果您对x86_64汇编感到惊讶,您还应该找到一个关于它的参考资料。从printf中
%p
的输出来看,您编译的是64位,但asm代码假定为32位。试一试
__asm__
__volatile__(
"movl %%rbx, %%rsi\n\t;"
"lodsb\n\t;"
: "=&S" (esi_out), "=&b" (ebx), "=&a" (eax)
: "1" (src1)
);
printf("src1 %c @ %p, esi_out: %x, eax: %x, ebx: %x\n",
*src1, src1, esi_out, eax, ebx);
您还应该将esi\u out
和ebx
声明为指针类型(void*
或char*
)或uintptpr\u t
)
发生的情况是,lodsb在64位模式下使用RSI作为其源地址,但您只将指针值的低32位放入RSI,因此它不包含有效地址,因此存在SEGFULT。正如Slagh所说,lodsb只修改寄存器(al)的低位8位。您应该屏蔽其他位(eax&0xff),清除rax(xor%rax,%rax),或者将eax声明为字符
如果您对x86_64汇编感到惊讶,您还应该找到一个关于它的参考资料。您有一些错误:
您使用的是64位体系结构,int
不够大,无法容纳指针。esi\u out
变量应为64位宽,或者最好使用ptrdiff\u t
。你应该把它命名为rsi\u out
64位模式下的lodsb
指令隐含地引用rsi
,而不是esi
。在movl%%ebx、%%esi
之前的指令中,您只设置了rsi
的下半部分,上半部分被隐式清除。将其更改为movq%%rbx、%%rsi
您有一些错误:
您使用的是64位体系结构,int
不够大,无法容纳指针。esi\u out
变量应为64位宽,或者最好使用ptrdiff\u t
。你应该把它命名为rsi\u out
64位模式下的
lodsb
指令隐含地引用rsi
,而不是esi
。在movl%%ebx、%%esi
之前的指令中,您只设置了rsi
的下半部分,上半部分被隐式清除。将其更改为movq%%rbx、%%rsi
谢谢,斯拉夫。EBX用src1的值初始化。gcc中内联asm指令的格式为_asm__(.data:outputs:inputs(:clobbred)),输出和输入控制asm部分的输入和输出。在这种情况下,输出是从esi(&S)读取esi_,从ebx(&b)读取ebx,从eax(&a)读取eax;输入是src1读入EBX,其中“1”表示参数$1,编译器将该参数分配给从$0开始的所有输入和输出。在这种情况下:{$0=esi_out,1=ebx,等等}谢谢,斯拉夫。EBX用src1的值初始化。gcc中内联asm指令的格式为_asm__(.data:outputs:inputs(:clobbred)),输出和输入控制asm部分的输入和输出。在这种情况下,输出是从esi(&S)读取esi_,从ebx(&b)读取ebx,从eax(&a)读取eax;输入是src1读入EBX wh