是什么导致这个程序出现段错误?

What causes this program to segmentation fault?

我正在尝试计算压入堆栈的数据的大小。尝试此操作时,我收到分段错误。我的 objective 是在 push 将任何数据放入堆栈之前将 ebp 指向 esp。在我将数据推入堆栈后,我以字节 ebp - esp 为单位测量所述数据的大小并存储在 ebx 中并使用 printf.

打印到标准输出

例如:

; compile with:
; nasm -f elf test.asm && gcc -m32 -o test test.o
section .text
global  main
extern printf

main:
    ; set the frame pointer to the beginning of the stack before-
    ; data is pushed.
    push  ebp
    mov   ebp, esp

    push  ebx
    push  0x00       ; <- null terminating byte/string truncation
    push  0x64636261 ; <- data

    mov   ebx, ebp
    sub   ebx, esp   ; start of stack - end of stack  = sizeof(data) stored in ebx

    push  ebx
    push  format_str
    call  printf
    add   esp, 8

    pop   ebp
    pop   ebx
    ret

section .data
format_str db "%s", 2, 0

编译此代码时,我收到输出:

Segmentation fault (core dumped)

预期输出:

5

首先,如果您希望项目返回其原始寄存器,则应按照它们被推入的 反向 顺序弹出项目。你这里有什么(删除了不相关的行):

push ebp
push ebx
pop ebp
pop ebx

不会将 ebp 恢复到其先前的值,这 非常 可能会导致在堆栈帧中向上移动时出现问题。


此外,您最好遵循从 ebp 恢复 esp 的正常做法,而不是盲目地添加 8。最后会是这样的:

; add esp, 8 ; not the normal way.
mov   esp, ebp
pop   ebp

这样做就不需要您手动计算需要从堆栈中取出多少字节,这种计算实际上是错误的,因为您没有考虑到您推送的所有内容。


最后,你这样做之前,你必须确保 esp 在正确的位置,这样 pop ebp 才能工作.这意味着弹出你推送的所有内容(除了 ebp 本身)。由于您(在 ebp 之后)推送了 ebx0x000000000c64636261ebxformat_str,您应该确保所有这些都是在尝试弹出 ebp.

之前离开堆栈

将所有这些考虑在内,您会得到类似的结果:

main:
    push  ebp
    mov   ebp, esp

    push  ebx           ; (+1)
    push  0x00          ; (+2)
    push  0x64636261    ; (+3)

    mov   ebx, ebp
    sub   ebx, esp

    push  ebx           ; (+4)
    push  format_str    ; (+5)
    call  printf
    add   esp, 16       ; (-5, -4, -3, -2)
    pop   ebx           ; (-1)

    mov   esp, ebp
    pop   ebp
    ret

每个 (+N) 注释代表一个已压入堆栈的 32 位值,(-N) 注释指示哪些指令反转这些压入。 add esp, 16 反转其中的四个(每个四个字节),并且这样做是因为我们不关心这些项目会发生什么。剩下最后的 pop 来恢复原来的 ebx(我们 关心)。

我认为,在这种情况下,esp 的最终重新加载是不必要的,因为它已通过前面的步骤恢复为正确的值。是否保留 prolog/epilog 一致性取决于您。