是什么导致这个程序出现段错误?
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
之后)推送了 ebx
、0x00000000
、0c64636261
、ebx
和 format_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 一致性取决于您。
我正在尝试计算压入堆栈的数据的大小。尝试此操作时,我收到分段错误。我的 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
之后)推送了 ebx
、0x00000000
、0c64636261
、ebx
和 format_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 一致性取决于您。