【问题标题】:Strange segmentation fault in assembly装配中奇怪的分段错误
【发布时间】:2011-05-06 04:03:31
【问题描述】:

我使用 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 ?? ()

知道为什么会这样吗?

【问题讨论】:

  • 我没有发布完整的程序,因为它很长......但是调用者中有一个退出标签。这只是导致seg错误的程序过程。如果你愿意,我可以发布整个节目。
  • 不需要。跳出这样的功能通常是不可能的(除非您确定您知道自己在做什么)。下面的答案更详细地描述了这一点。

标签: x86 segmentation-fault fault


【解决方案1】:

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

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

mov <destination>, <source>

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

mov <source>, <destination>

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

现在看这行代码:

mov ebp, eax

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


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

jz exit

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

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

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

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

【讨论】:

  • 感谢您的快速回复。我没有在这里发布完整的代码。在调用者程序员中,有一个退出标签。
  • 我应该发布整个程序吗??
  • 我添加了对实际问题的扩展说明。
  • 我明白了你的观点并同意你的观点。但奇怪的是,如果我省略 cmp eax,0 和 jz 退出行,程序在遇到文件结尾(即 eax=0)时会出现段错误.它无法返回给调用者。有了这些行(cmp eax,0 jz exit),遇到输入文件结尾的过程能够跳转到退出标签而不会出现段错误。虽然我认为后者是错了,因为我还没有清理堆栈。
  • 啊,我当时误解了问题所在。你能改写一下吗,我再看一遍。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多