X86 装配中的奇异分段故障

X86 装配中的奇异分段故障,x86,segmentation-fault,fault,X86,Segmentation Fault,Fault,我使用nasm编译器和ld链接器用汇编语言编写了一个hexdump实用程序。该程序应该为任何输入文件转储十六进制值。但是它在一个特定的过程“LoadBuff”中出错。LoadBuff的功能是将输入读取到16字节的缓冲区。代码是 LoadBuff: push ebx push edx push eax mov eax,3 ;sys_read call mov ebx,0

我使用nasm编译器和ld链接器用汇编语言编写了一个hexdump实用程序。该程序应该为任何输入文件转储十六进制值。但是它在一个特定的过程“LoadBuff”中出错。LoadBuff的功能是将输入读取到16字节的缓冲区。代码是

LoadBuff:
    push ebx                 
    push edx
    push eax

    mov eax,3       ;sys_read call      
    mov ebx,0               ;read from standard input
    mov ecx,Buff            ;pass the buffer adress
    mov edx,BuffLen         ;pass the number of bytes to be read at a time
    int 80h                 ;call the linux kernel
    mov ebp,eax
    ;cmp eax,0              ;number of characters read is returned in eax
    ;jz exit                ;if zero character is returned i.e end of iinput file      
                            ;jump to exit

    xor ecx,ecx 
    pop eax
    pop edx
    pop ebx
    ret
如果这些线

;cmp eax,0                   
;jz exit                      
如果没有注释,代码运行正常,没有任何seg错误。但是,当我注释它并在调用程序中包含这些行,以便在调用程序中进行相同的比较,而不是在这里,此过程会出现seg错误

gdb回溯

#0  0x00000000 in ?? ()

知道为什么会这样吗?

您使用的是NASM,但没有指定是使用英特尔风格的语法还是AT&T风格的语法。然而,看看您的示例代码,我猜它是英特尔风格的

在英特尔风格的语法中,像
mov
这样的操作如下:

mov <destination>, <source>
如果您使用的是英特尔风格的语法(我想您是因为AT&T风格的语法是
mov%ebp,%eax
),那么您将寄存器
eax
的内容移动到
ebp
ebp
传统上用作“基本指针”。。。注意这里的“指针”一词。。。而且通常都是这样使用的。当您在eax中获得0时,您正在用空指针覆盖现有的基指针。怪诞的恶作剧接踵而至


然而,这不是唯一的问题。另一个问题是:

jz exit
我在您发布的代码中的任何地方都看不到退出标签,因此您正在跳转到过程之外的某个地方(否则汇编程序会发出呜呜声)。在传递堆栈清理代码的过程中,堆栈处于未知状态。基本上,您已经将三个寄存器的内容推送到堆栈上,并将它们放在其他例程不期望的地方

问题是你跳过了清理代码。在您的程序开始时,您正在按
ebx
edx
eax
。在程序结束时,正确地按相反顺序弹出它们(
eax
edx
ebx
)。这使得堆栈在退出时的状态与在进入时的状态相同,并且依赖此状态的代码被设置为按预期运行

但是,
jz
跳过了该点,因此无论你去哪里,堆栈上都有三个不应该存在的值。您必须跳转到清理代码,而不是跳过它


通常的规则是,在你的程序中,你推什么。这条规则有(极少数)例外情况,但它们的出现频率不足以分散您的注意力。

谢谢您的快速回复。我还没有在这里发布完整的代码。在caller programmer中,有一个退出标签。我应该发布整个程序吗?我已经添加了对实际问题的详细说明。我明白你的观点并同意你的观点。但奇怪的是,如果我省略cmp eax、0和jz退出行,程序在遇到文件结尾(即eax=0)时会出现seg故障。它无法返回调用方。使用这些行(cmp eax,0 jz出口)在那里,遇到输入文件结尾的过程能够跳转到退出标签,而不会出现seg错误。尽管我认为后者是错误的,因为我没有清理堆栈。啊,我当时误解了问题所在。你能重写一下吗,我再看一看。我没有发布完整的程序,因为它很长……但有一个退出调用方中的标签。这只是导致seg故障的程序的一个过程。如果您愿意,我可以发布整个程序。无需。从这样的函数中跳出通常是不可能的(除非您确定自己知道自己在做什么)。下面的回答对此进行了更详细的描述。
mov ebp, eax
jz exit