为什么这个功能有效但给我一个分段错误?
Why does this function work but give me a segmentation fault?
.code32
.text
.globl _start
_start:
; .globl main
.extern printf
pushl %ebp
movl %esp, %ebp
; subl , %esp
movl 4(%ebp), %eax
; movl 12(%ebp), %ebx
; movl [=10=], %ecx
cmp , %eax
jne argCount
pushl %eax
pushl $msg2
call printf
add , %esp
movl %ebp, %esp
popl %ebp
; movl , %eax
; movl [=10=], %ebx
; int [=10=]x80
call exit
argCount:
pushl %ebp
movl %esp, %ebp
pushl $msg3
call printf
add , %esp
movl %ebp, %esp
popl %ebp
ret
.data
; .asciz "Arg = %s"
msg2: .asciz "Arg Count = %d\n"
msg3: .asciz "This program takes 1 argument -> sizeOfArray\n"
如果命令行上的参数计数不等于 2,为什么函数 argCount 可以正确执行,但给我一个分段错误?否则程序的整体控制流程似乎工作正常。也许函数序言或尾声有问题?
眼前的问题是 argCount
末尾的 ret
去错了地方,因为它没有有效的 return 地址。
此外,如果您打算使用 libc,则应使用 main
,并且还要注意按照 ABI 保持所需的堆栈对齐。使用帧指针是可选的,通常没有任何帮助,所以我省略了它。
这是一个可能的版本:
.globl main
main: # main because we use libc
subl , %esp # stack alignment
movl 16(%esp), %eax # argc
cmpl , %eax
movl $msg3, (%esp) # preset for msg3
jne argCount # if argc!=2 use that
movl $msg2, (%esp) # otherwise msg2
movl %eax, 4(%esp) # and argc
argCount:
call printf # print
call exit # exit
.data
msg2: .asciz "Arg Count = %d\n"
msg3: .asciz "This program takes 1 argument -> sizeOfArray\n"
确保使用 gcc
编译和 link 以便正确引入 libc,如果您在 64 位环境中生成 32 位程序,则提供 -m32
(因为此代码仅适用于 32 位)。
这些行是错误的:
cmp , %eax
jne argCount
CALL
做了两件事:
- 将return地址压入堆栈
- 无条件跳转到调用的函数。
如果您使用 Jcc
或 JMP
指令调用函数,您将需要手动将 return 地址压入堆栈以使您的代码正常工作。
push return_address
jmp argCount
return_address:
然而,如果您使用条件跳转,这将导致问题,因为当您编写代码时,您无法预测分支是否被采用(因此 RET
被调用的函数已经为您从堆栈中弹出 return 地址,否则您需要自己处理)。
你应该做的是:
cmp , %eax
je skip_argcount_call
call argCount
skip_argcount_call:
.code32
.text
.globl _start
_start:
; .globl main
.extern printf
pushl %ebp
movl %esp, %ebp
; subl , %esp
movl 4(%ebp), %eax
; movl 12(%ebp), %ebx
; movl [=10=], %ecx
cmp , %eax
jne argCount
pushl %eax
pushl $msg2
call printf
add , %esp
movl %ebp, %esp
popl %ebp
; movl , %eax
; movl [=10=], %ebx
; int [=10=]x80
call exit
argCount:
pushl %ebp
movl %esp, %ebp
pushl $msg3
call printf
add , %esp
movl %ebp, %esp
popl %ebp
ret
.data
; .asciz "Arg = %s"
msg2: .asciz "Arg Count = %d\n"
msg3: .asciz "This program takes 1 argument -> sizeOfArray\n"
如果命令行上的参数计数不等于 2,为什么函数 argCount 可以正确执行,但给我一个分段错误?否则程序的整体控制流程似乎工作正常。也许函数序言或尾声有问题?
眼前的问题是 argCount
末尾的 ret
去错了地方,因为它没有有效的 return 地址。
此外,如果您打算使用 libc,则应使用 main
,并且还要注意按照 ABI 保持所需的堆栈对齐。使用帧指针是可选的,通常没有任何帮助,所以我省略了它。
这是一个可能的版本:
.globl main
main: # main because we use libc
subl , %esp # stack alignment
movl 16(%esp), %eax # argc
cmpl , %eax
movl $msg3, (%esp) # preset for msg3
jne argCount # if argc!=2 use that
movl $msg2, (%esp) # otherwise msg2
movl %eax, 4(%esp) # and argc
argCount:
call printf # print
call exit # exit
.data
msg2: .asciz "Arg Count = %d\n"
msg3: .asciz "This program takes 1 argument -> sizeOfArray\n"
确保使用 gcc
编译和 link 以便正确引入 libc,如果您在 64 位环境中生成 32 位程序,则提供 -m32
(因为此代码仅适用于 32 位)。
这些行是错误的:
cmp , %eax
jne argCount
CALL
做了两件事:
- 将return地址压入堆栈
- 无条件跳转到调用的函数。
如果您使用 Jcc
或 JMP
指令调用函数,您将需要手动将 return 地址压入堆栈以使您的代码正常工作。
push return_address
jmp argCount
return_address:
然而,如果您使用条件跳转,这将导致问题,因为当您编写代码时,您无法预测分支是否被采用(因此 RET
被调用的函数已经为您从堆栈中弹出 return 地址,否则您需要自己处理)。
你应该做的是:
cmp , %eax
je skip_argcount_call
call argCount
skip_argcount_call: