为什么这个汇编代码出现Segmentation Fault?

时间:2017-07-22 04:59:51

标签: assembly segmentation-fault nasm

为什么此代码会出现分段错误? (它由Intel Assemble语法编码)

结果是这样的。

return value : 80






[Dump]
eax : 0x00000012
ebx : 0x00000004
ecx : 0x00000400
edx : 0x4010a980
Segmentation fault (core dumped)

我认为我有足够的代码来防止分段错误。 每个函数都有序言和结尾来维护堆栈内存。

但是,它发生了分段错误错误。

[附加说明] 如果我删除了代码' mov ebx,4',则删除了分段错误错误。 (但结果不符合我的意图)

extern printf

segment .data
dumpmsg db      10,10,10,10,10,10,'[Dump]',10,'eax : 0x%0.8x',10,'ebx : 0x%0.8x',10,'ecx : 0x%0.8x',10,'edx : 0x%0.8x',10,00
msg     db      'return value : %d', 10, 00

segment .bss

segment .text
global  main
main:
        push    ebp
        mov     ebp,    esp

        push    20
        call    pig
        add     esp,    4

        push    eax
        push    msg
        call    printf

        call    print_x
        leave
        ret

pig:
        push    ebp
        mov     ebp,    esp

        mov     eax,    [ebp+8]
        mov     ebx,    4
        mul     ebx

        leave
        ret

print_x:
        push    ebp
        mov     ebp,    esp

        push    edx
        push    ecx
        push    ebx
        push    eax
        push    dumpmsg
        call    printf

        leave
        ret

1 个答案:

答案 0 :(得分:3)

  

如果删除代码' mov ebx,4',则删除了分段错误错误。 (但结果不符合我的意图)

EBX寄存器几乎是所有x86调用约定中的callee-save。这意味着叶子函数不能破坏其值。如果需要使用EBX,则必须将其原始值保存在函数顶部并在结束时将其恢复。这通常通过PUSH + POP指令来完成,以便将寄存器的值保存在堆栈中。

您的pig功能正在使用EBX指令进行mov ebx, 4,但正在注意保存和恢复其原始值。这违反了调用约定,因为您正在与C代码进行互操作。

修复很简单:

pig:
    push    ebp
    mov     ebp,    esp
    push    ebx

    mov     eax,    [ebp+12]
    mov     ebx,    4
    mul     ebx

    pop     ebx
    leave
    ret

或者,只需使用ECX寄存器来保存除数,因为这是一个调用者保存(因此可以自由地删除):

pig:
    push    ebp
    mov     ebp,    esp

    mov     eax,    [ebp+12]
    mov     ecx,    4
    mul     ecx

    leave
    ret
  

每个函数都有序言和结尾来维护堆栈内存。

这真的没有必要。除了检索参数之外,pig函数根本不使用堆栈,因此您可以将其简单地写为:

pig:
    mov     eax,    [esp+4]
    mov     ecx,    4
    mul     ecx
    ret

基本指针(EBP)的使用同样可以通过直接使用堆栈指针(ESP)从其他函数中删除,甚至是那些使用堆栈的函数。这与编译器将EBP作为额外的寄存器释放并保存序言/结尾膨胀相同的优化。但如果你以这种方式做得更舒服,那么除了表现之外它不会受到任何伤害。

必不可少的是您遵循调用约定。 EAXEDXECX个寄存器可以在叶子函数内自由地删除;所有其余的都需要明确保存并恢复到原始值。在堆栈上分配空间时,需要确保释放它。你是如何灵活的,无论是sub esp, xx ... add esp, xx还是序言......你的结局。