为什么这个功能有效但给我一个分段错误?

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 做了两件事:

  1. 将return地址压入堆栈
  2. 无条件跳转到调用的函数。

如果您使用 JccJMP 指令调用函数,您将需要手动将 return 地址压入堆栈以使您的代码正常工作。

push return_address
jmp argCount
return_address:

然而,如果您使用条件跳转,这将导致问题,因为当您编写代码时,您无法预测分支是否被采用(因此 RET被调用的函数已经为您从堆栈中弹出 return 地址,否则您需要自己处理)。

你应该做的是:

cmp , %eax
je skip_argcount_call
call argCount
skip_argcount_call: