汇编代码实现DFA状态机时出现段错误的解决方法
x86-64汇编DFA段错误修复方案
核心问题分析
原代码触发段错误及逻辑失效的关键原因:
- 输入缓冲区溢出:
.bss段仅为input分配2字节,输入较长字符串时会越界写入非法内存区域,触发段错误。 - 字符验证逻辑完全错误:ASCII转二进制后,未正确判断输入是否为0/1,反而用两条
cmov指令无条件将%ebx设为%edx,导致状态完全混乱,甚至可能让跳转表索引超出0-3的合法范围,跳转到非法地址。 - DFA状态转移缺失:当前代码的状态跳转后仅设置
%edx,未根据输入的0/1执行正确的状态转移,完全违背DFA核心逻辑。 - 未处理EOF情况:
getchar返回EOF(值为-1)时,未做判断会被当成字符处理,引发后续错误。
修复后的代码
保留跳转表、getchar@PLT/puts@PLT核心结构,修复所有问题:
.section .data input_prompt: .asciz "Enter a string of 0s and 1s: " invalid_message: .asciz "ERROR: invalid input\n" EEPrompt: .asciz "Input accepted\n" NotEEPrompt: .asciz "Input rejected\n" .section .bss .lcomm input, 256 # 分配256字节缓冲区,避免溢出 .section .text .globl main main: # 打印输入提示 lea input_prompt, %rdi call puts@PLT movl $0, %ebx # 初始状态:EE(0个0,0个1) lea input, %rsi # 指向输入缓冲区起始 movl $0, %edx # 临时变量,用于状态转移 read_loop: call getchar@PLT # 处理EOF(用户按Ctrl+D) cmpl $-1, %eax je input_finalize # 处理换行,结束输入 cmpb $0x0A, %al je input_finalize # ASCII转二进制:'0'->0,'1'->1 sub $0x30, %al # 验证输入是否为0或1 cmpb $0, %al jl invalid_input # 小于0,非法字符 cmpb $1, %al jg invalid_input # 大于1,非法字符 # 保存输入到缓冲区(检查是否溢出) cmpq %rsi, input+255 # 缓冲区剩余空间判断 je invalid_input # 缓冲区满,视为非法 movb %al, (%rsi) inc %rsi # 根据当前状态和输入,计算下一个状态 # 状态编码:0=EE, 1=OE, 2=OO, 3=EO # 输入0:状态 ^ 1;输入1:状态 ^ 2 movl %ebx, %edx cmpb $0, %al je input_zero xorl $2, %edx # 输入1,状态异或2 jmp update_state input_zero: xorl $1, %edx # 输入0,状态异或1 update_state: movl %edx, %ebx # 通过跳转表进入对应状态处理 jmp *jump_table(,%rbx,8) jump_table: .quad ee_state .quad oe_state .quad oo_state .quad eo_state # 各状态仅需回到读循环,状态已在update_state中更新 ee_state: jmp read_loop oe_state: jmp read_loop oo_state: jmp read_loop eo_state: jmp read_loop invalid_input: lea invalid_message, %rdi call puts@PLT jmp exit_with_code accepted_input: lea EEPrompt, %rdi call puts@PLT jmp exit_with_code rejected_input: lea NotEEPrompt, %rdi call puts@PLT jmp exit_with_code input_finalize: # 检查最终状态是否为EE(0) cmpl $0, %ebx je accepted_input jmp rejected_input exit_with_code: # 根据退出类型设置状态码:成功0,失败1 movq $60, %rax cmpq $accepted_input, %rsp+8 # 简单判断是否从accepted_input跳转 je exit_success movl $1, %rdi syscall exit_success: xor %rdi, %rdi syscall
关键修复点说明
- 扩大输入缓冲区:用
.lcomm input, 256分配256字节缓冲区,同时新增缓冲区溢出检查,避免越界写入。 - 修复字符验证:新增
cmpb判断输入转二进制后是否为0或1,非法字符直接跳转到错误处理。 - 实现正确DFA状态转移:根据输入的0/1,通过异或操作更新状态(0翻转0的计数奇偶,1翻转1的计数奇偶),确保状态符合EE/OE/OO/EO的定义。
- 处理EOF情况:新增对
getchar返回-1的判断,避免EOF被当成有效字符处理。 - 简化状态处理逻辑:跳转表的各状态标签仅需回到读循环,状态已在转移时更新,无需重复设置
%edx。
内容的提问来源于stack exchange,提问作者Erique
相关产品推荐
相关产品推荐

