x86汇编逐字节读取程序调试器中正常,直接运行返回错误值
x86汇编程序返回值异常原因分析
这段x86汇编程序的功能是逐字节读取标准输入的十进制ASCII数字,转换为整数后存入EDI寄存器,最终通过_exit系统调用将EDI的值作为进程退出码返回。但出现以下异常:
- GDB逐步调试时,EDI寄存器的值正确(输入
2后直接Ctrl+D,EDI为2) - 直接运行程序(输入
2→回车→Ctrl+D),通过echo $?查看返回值为238(0xEE)
汇编代码
global _start %macro kernel 4 mov eax, %1 mov ebx, %2 mov ecx, %3 mov edx, %4 int 80h %endmacro section .bss symbol resb 1 section .text _start: mov esi, 10 xor edi, edi .loop: kernel 3, 0, symbol, 1 ; load 1 char from STDIN into symbol test eax, eax ; nothing loaded - EOF jz .quit xor ebx, ebx mov bl, [symbol] sub bl, '0' cmp bl, 9 jg .quit ; not a number mov eax, edi ; previously accumulated number mul esi ; eax *= 10 lea edi, [eax + ebx] jmp .loop .quit: mov eax, 1 mov ebx, edi int 80h
编译与运行命令
$ nasm -g -f elf32 st3-18a.asm $ ld -g -m elf_i386 st3-18a.o -o st3-18a $ ./st3-18a 2[Enter] Ctrl-d
异常核心原因:条件跳转指令使用错误
问题出在判断输入是否为数字的跳转逻辑上:
- 当输入
2后按回车,程序会读取到两个字符:'2'(ASCII 50)和'\n'(ASCII 10) - 处理
'\n'时,sub bl, '0'后bl的值为10 - 48 = -38,对应8位无符号数是0xEE(238) - 代码中使用
cmp bl, 9后接jg .quit:jg是有符号数的大于跳转指令,对于有符号数来说,-38 < 9,因此不会跳转到.quit,反而继续执行后续的数值计算 - 后续计算中,
eax = 2 * 10 = 20,lea edi, [eax + ebx]得到edi = 20 + (-38) = -18,对应32位值为0xFFFFFFEE - Linux的
_exit系统调用(eax=1)只返回ebx的低8位作为进程退出码,因此最终返回0xEE(238)
而GDB调试时,若直接输入2后按Ctrl+D,程序仅读取到'2'就遇到EOF,不会触发错误的数值计算,因此EDI值正确。
修复方案
将jg .quit替换为无符号数大于跳转指令ja .quit:
cmp bl, 9 ja .quit ; not a number
ja会按无符号数比较,0xEE(238)大于9,此时会跳转到.quit,避免错误计算EDI的值。
内容的提问来源于stack exchange,提问作者user4035
相关产品推荐
相关产品推荐

