x86 AT&T汇编重复减法除法程序异常问题求助
x86 AT&T汇编大整数重复减法除法程序异常修复
需求与问题
程序需求:从STDIN读取32字节作为被除数,再读取32字节作为除数,采用重复减法除法算法实现大整数除法,最终通过STDOUT输出32字节的商和32字节的余数。
程序异常表现:有时可正常运行,无需修改代码仅重新汇编链接后,会出现无法完整读取输入数据的情况,后续输出结果错误。
原代码
EXIT_NR = 1 READ_NR = 3 WRITE_NR = 4 STDOUT = 1 STDIN = 0 EXIT_CODE_SUCCESS = 0 .data dividend: .space 32 divisior: .space 32 quotient: .space 32 rest: .space 32 .text .global _start _start: read: movl $READ_NR, %eax movl $STDIN, %ebx movl $dividend,%ecx movl $64, %edx int $0x80 clc cmp $0,%eax jle end movl $31,%esi div_loop: clc cmp $0,%esi je done movb dividend(%esi), %al movb divisior(%esi),%bl xor %dl,%dl sub_loop: sbbb %bl,%al jc odtw inc %dl jmp sub_loop odtw: movb %dl,result(%esi) addb %bl,%al movb %al,rest(%esi) dec %esi jmp div_loop done: movl $WRITE_NR,%eax movl $STDOUT,%ebx movl $result, %ecx movl $64, %edx int $0x80 jmp read end: movl $EXIT_NR, %eax movl $EXIT_CODE_SUCCESS, %ebx int $0x80
示例输入与错误输出
输入(64字节十六进制):
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0b |................| 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 |................|
错误输出(64字节十六进制,商+余数):
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 |................| 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 |................|
(注:此示例输出数值巧合正确,但逻辑错误会导致多字节大整数计算失败,且程序稳定性差)
问题分析与修复
1. 未定义符号result导致内存访问异常
原代码中使用了未在.data段定义的result符号,汇编链接时会将其解析为不确定的内存地址,导致程序行为不稳定(有时误打误撞访问到合法内存,有时则破坏其他数据)。应将result替换为已定义的quotient。
2. Read系统调用错误处理不严谨
原代码仅检查read返回值是否<=0,但未验证是否读取到完整的64字节。若输入数据不完整,程序会使用未初始化的内存进行计算,导致错误结果。需添加读取字节数的校验。
3. 重复减法算法逻辑完全错误
原代码按单个字节独立执行减法,这不符合大整数除法的逻辑。正确的重复减法应针对整个32字节的大整数进行操作:循环将被除数减去除数,每成功减一次商加1,直到被除数小于除数,此时的被除数即为余数。
修复后的代码
EXIT_NR = 1 READ_NR = 3 WRITE_NR = 4 STDOUT = 1 STDIN = 0 EXIT_CODE_SUCCESS = 0 EXIT_CODE_ERROR = 1 .data dividend: .space 32 divisor: .space 32 quotient: .space 32 rest: .space 32 .text .global _start _start: read: # 读取64字节:32字节被除数 + 32字节除数 movl $READ_NR, %eax movl $STDIN, %ebx movl $dividend, %ecx movl $64, %edx int $0x80 # 检查读取是否成功且完整 cmpl $64, %eax jne exit_error # 初始化商为0 movl $0, %edi clear_quotient: movb $0, quotient(%edi) incl %edi cmpl $32, %edi jne clear_quotient # 将被除数复制到余数(后续操作余数) movl $0, %edi copy_dividend_to_rest: movb dividend(%edi), %al movb %al, rest(%edi) incl %edi cmpl $32, %edi jne copy_dividend_to_rest div_loop: # 比较余数和除数:余数 >= 除数则继续减 call compare_rest_divisor jl done_div # 余数 = 余数 - 除数 call subtract_divisor_from_rest # 商 += 1(32字节大整数加1) call increment_quotient jmp div_loop done_div: # 输出商(32字节)+余数(32字节) movl $WRITE_NR, %eax movl $STDOUT, %ebx movl $quotient, %ecx movl $64, %edx int $0x80 jmp read exit_error: movl $EXIT_NR, %eax movl $EXIT_CODE_ERROR, %ebx int $0x80 end: movl $EXIT_NR, %eax movl $EXIT_CODE_SUCCESS, %ebx int $0x80 # 辅助函数:比较rest和divisor(大整数),rest >= divisor则CF=0,否则CF=1 compare_rest_divisor: movl $31, %esi compare_loop: movb rest(%esi), %al movb divisor(%esi), %bl cmpb %bl, %al jg compare_ge jl compare_lt dec %esi jns compare_loop # 两数相等 clc ret compare_ge: clc ret compare_lt: stc ret # 辅助函数:rest = rest - divisor(带借位的大整数减法) subtract_divisor_from_rest: movl $31, %esi clc subtract_loop: movb rest(%esi), %al sbbb divisor(%esi), %al movb %al, rest(%esi) dec %esi jns subtract_loop ret # 辅助函数:quotient += 1(大整数加1) increment_quotient: movl $31, %esi clc inc_loop: movb quotient(%esi), %al adcb $1, %al movb %al, quotient(%esi) jnc inc_done dec %esi jns inc_loop inc_done: ret
说明
修复后的代码实现了正确的32字节大整数重复减法除法:
- 严格校验输入读取的完整性,避免未初始化内存访问
- 采用大整数比较、减法、自增的辅助函数实现核心逻辑
- 修复了符号未定义的问题,确保内存访问合法
内容的提问来源于stack exchange,提问作者Enumero
相关产品推荐
相关产品推荐

