是什么导致该程序分段错误?

问题描述

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

例如:

; 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恢复到先前的值,并且非常可能会导致在堆栈帧中向上移动的问题。


另外,您最好遵循从esp恢复ebp的常规做法,而不是盲目地添加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的最终重载在这种情况下是不必要的,因为它已通过前面的步骤恢复为正确的值。是否保留序言/后记的一致性取决于您自己。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...