5

我已经使用 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                      

未注释代码运行良好,没有任何段错误。但是,当我评论它并将这些行包含在调用者中以便在调用者中而不是在这里进行相同的比较时,此过程出现段错误。

gdb 回溯给出

#0  0x00000000 in ?? ()

知道为什么会这样吗?

4

1 回答 1

1

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

在 Intel 风格的语法中,类似的操作是这样mov工作的:

mov <destination>, <source>

换句话说,他们试图模仿“目的地=来源”的思维方式。在 AT&T 语法中则相反:

mov <source>, <destination>

换句话说,他们认为它就像您正在阅读“将源移至目的地”。

现在看这行代码:

mov ebp, eax

如果您使用的是 Intel 风格的语法(我认为您是因为 AT&T 风格的语法是mov %ebp, %eax),那么您正在将 register 的内容移动eaxebp. ebp传统上用作“基本指针”...请注意那里的“指针”一词...并且通常完全按此使用。当您在 eax 中获得 0 时,您将使用空指针覆盖现有的基指针。古怪的hijinks接踵而至。


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

jz exit

我在您发布的代码中的任何地方都没有看到退出标签,因此您在程序之外的某个地方(否则汇编程序会抱怨)跳跃。在此过程中,您将传递堆栈清理代码,使堆栈处于未知状态。您基本上已经将三个寄存器的内容压入堆栈,并将它们留在其他例程不期望它们的位置。

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

然而,这jz跳过了那个点,所以无论你去哪里,堆栈上都有三个不应该存在的值。你必须跳到的清理代码,而不是过去

一般规则总是弹出您在过程中推送的内容。这条规则有(极少数)例外,但它们发生的频率不足以让你现在用它分散注意力。

于 2011-05-06T04:15:47.403 回答