在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