You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

x86汇编程序segmentation fault问题排查求助

问题描述

我是汇编语言新手,掌握的相关知识有限。我编写了一段Linux环境下的x86汇编程序,预期实现:读取用户输入的message,先输出"Hello, World!\n",再输出用户输入的内容。但程序运行出现segmentation fault,我怀疑问题出在_get_buffer_size标签处。以下是程序代码及各标签说明:

# TODO: Read message from input, output "Hello, World!", and output message on newline 

.section .data                  # Data that will be used by program


  ## * CONSTANTS * ##

  .equ LINUX_SYSCALL, 0x80      # Interrupt ID to make syscall on kernel (GNU/Linux)
  
  ## * SYCALL IDs * ##
  .equ EXIT_SYS, 1              # SYSCALL Number for exiting
  .equ OPEN_SYS, 5              # SYSCALL Number for opening files
  .equ READ_SYS, 3              # SYSCALL Number for reading from files
  .equ WRITE_SYS, 4             # SYSCALL Number for writing to files
  .equ CLOSE, 6                 # SYSCALL Number to close files

  ## * File modes * ##
  .equ READ_MODE, 0              # Reading mode for files
  .equ WRITE_MODE, 03101         # Writing mode for files

  ## * File permissions * ##
  .equ DEFAULT_PERMISSION, 0666  # Default permission to interact with files

  ## * Default file descriptors * ##
  
  .equ STDIN, 0                  # Default GNU/Linux input file
  .equ STDOUT, 1                 # Default GNU/Linux output file
  .equ STDERR, 2                 # Default GNU/Linux error-log file

  ## * Constant variables * ##

  OUTPUT_MSG:                   # "Hello, World!" message
    .ascii "Hello, World!
"

  SIZE_OF_MSG:                  # Size of "Hello, World!"
    .long 14                    # remember to count '\n' as 1 byte

.section .bss                   # Reserved memory area
  .equ BUFFER_SIZE, 500         # Size of buffer that will be read from STDIN
  .lcomm BUFFER, 500            # Buffer that will be read from STDIN
  

.section .text                  # Code area
.globl _start                   # Starting function definiton



# @brief: Label to terminate program successfuly
_exit_success:
  movl $EXIT_SYS, %eax          
  movl $0, %ebx
  int $LINUX_SYSCALL

# @brief: Label to output `message` that stored in `%ecx`
# @param: `%ecx` must have `message` to output
# @param: `%edx` must have size of `message`
_output_msg:
  movl $WRITE_SYS, %eax
  movl $STDOUT, %ebx
  int $LINUX_SYSCALL
  ret

# @brief: Label to get input `buffer` from STDIN
# @param: `%ecx` must have `buffer` to store data
# @param: `%edx` must have size of `buffer` which will be used for data
_read_msg:
  movl $READ_SYS, %eax
  movl $STDIN, %ebx
  int $LINUX_SYSCALL
  ret


# @brief: Label to get size of a `buffer`
# @param: `%eax` must have pointer to `buffer`'s first char
# @return: size of buffer will be stored in `%edx`
_get_buffer_size:
  movl $0, %edx
  jmp .start_buffer_size
  ret

.start_buffer_size:
  incl %eax
  incl %edx
  cmpl $0, (%eax)   
  jnz .start_buffer_size
  ret

# @brief: _start function to start program
_start:

  ## * Get input message * ##
  movl $BUFFER, %ecx
  movl $BUFFER_SIZE, %edx
  call _read_msg

 
  ## * Output constant message * ##
  movl $OUTPUT_MSG, %ecx        # Store message in %ecx parameter
  movl $SIZE_OF_MSG, %edx       # Store size of message in %edx parameter
  call _output_msg              # Output the constant message

  ## * Output message that was read * ##
  movl $BUFFER, %ecx
  call _get_buffer_size 
  call _output_msg

  jmp _exit_success             # Terminate program

标签说明

  • _exit_success:用于正常终止程序,EXIT_SYS值为1,LINUX_SYSCALL值为0x80。
  • _output_msg:输出%ecx中存储的消息,要求%edx存放消息长度,WRITE_SYS值为4,STDOUT值为1。
  • _read_msg:从STDIN(值为0)读取输入,要求%ecx存放BUFFER,%edx存放BUFFER的预留大小。
  • _get_buffer_size:获取buffer长度,要求%eax存放buffer首字符指针,返回值(buffer长度)存储在%edx中。

备注:我知道汇编里标准函数调用不是这么写的,只是栈操作对我来说太复杂,所以自己用寄存器传递参数和返回值。现在需要排查程序出现段错误的原因。


问题排查与修复

核心错误点

  1. _get_buffer_size参数传递错误
    调用该标签时,你把BUFFER地址放到了%ecx,但标签要求参数存在%eax。此时%eax里是read系统调用的返回值(实际读取字节数),不是buffer指针,导致后续访问(%eax)时触发非法内存访问,直接引发段错误。

  2. _get_buffer_size逻辑缺陷

    • 初始跳转后直接incl %eax,跳过了buffer第一个字符,不仅长度计算少1,还会在buffer为空时直接越界。
    • 循环依赖0终止符判断结束,但read系统调用不会自动在输入末尾添加0。如果用户输入不含0,循环会持续读取非法内存,最终触发段错误。
  3. 常量消息输出的地址/数值混淆
    movl $SIZE_OF_MSG, %edx把SIZE_OF_MSG的标签地址存入%edx,而非它存储的数值14。正确写法是movl SIZE_OF_MSG, %edx(去掉$),用于加载内存中的数值。

  4. 未利用read的返回值
    read系统调用会把实际读取的字节数存在%eax中,这是最可靠的输入长度,完全可以直接用于后续输出,无需手动计算长度。

修复方案

方案1:简化逻辑,直接用read返回值(推荐)

去掉_get_buffer_size的调用,利用read的返回值作为输出长度,彻底避免手动计算的问题:

_start:
  ## * Get input message * ##
  movl $BUFFER, %ecx
  movl $BUFFER_SIZE, %edx
  call _read_msg
  movl %eax, %edx  # 保存read返回的实际读取字节数到%edx

  ## * Output constant message * ##
  movl $OUTPUT_MSG, %ecx
  movl SIZE_OF_MSG, %edx  # 加载SIZE_OF_MSG存储的数值14
  call _output_msg

  ## * Output message that was read * ##
  movl $BUFFER, %ecx
  # 直接使用之前保存的%edx作为长度
  call _output_msg

  jmp _exit_success

方案2:修复_get_buffer_size(不推荐,依赖输入含0终止符)

如果一定要保留手动计算长度的逻辑,需修复参数传递和循环逻辑:

# 修复后的_get_buffer_size
_get_buffer_size:
  movl $0, %edx
.start_buffer_size:
  cmpl $0, (%eax)   # 先检查当前字符是否为0
  je .end_buffer_size
  incl %eax
  incl %edx
  jmp .start_buffer_size
.end_buffer_size:
  ret

# 调用时的正确参数传递
_start:
  # ... 其他代码不变 ...

  ## * Output message that was read * ##
  movl $BUFFER, %eax  # 把buffer地址放到%eax,符合标签要求
  call _get_buffer_size
  movl $BUFFER, %ecx  # 恢复%ecx为buffer地址(因为_get_buffer_size修改了%eax)
  call _output_msg

  jmp _exit_success

内容的提问来源于stack exchange,提问作者cMard

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 04:02:03