NASM x86_64 Linux递增循环打印代码故障排查请求
问题排查与修复方案
咱们一步步拆解你代码里的问题并修复——你这里混淆了32位和64位系统调用规则,对write系统调用的寄存器用法有误解,还缺少了数值初始化与ASCII转换的关键环节,导致代码无法正常运行。
核心问题分析
1. 错误的系统调用号
在x86_64 Linux环境中,syscall指令使用的系统调用号和32位的int 0x80完全不同:
- 32位下
write的调用号是4,但64位下write的调用号是1。你设置mov rax,4会触发错误的系统调用(实际是sys_writev),直接导致行为异常。
2. 对rsi寄存器的用途理解错误
write系统调用的64位参数规则是:
rax=1:调用号(标识要执行write)rdi=1:文件描述符(stdout,这部分你是对的)rsi:指向要写入数据的内存指针,而非你要打印的数值本身rdx:写入的字节数(你设的1是对的,因为要打印单个字符)
你把rsi当成了要递增的数值来操作,直接add rsi,1后作为指针传入write,这会让程序访问随机内存地址(程序启动时rsi初始值未定义),大概率触发段错误。
3. 未初始化计数寄存器
程序启动时通用寄存器的初始值是未定义的,直接对rsi做递增操作会得到随机起始值,完全不符合你“从1开始打印10次”的预期。
4. 缺少整数到ASCII的转换
整数1的二进制值是0x01,对应ASCII不可见控制字符;要打印可见的数字字符,必须把整数加上0x30(即ASCII的'0'),比如1→0x31(对应字符'1')。
修复后的代码(实现打印1-10的可见数字)
section .data digit db 0 ; 存储转换后的单个数字字符 char_one db '1' ; 用于打印10的十位 char_zero db '0' ; 用于打印10的个位 section .text global _start _start: mov rcx, 1 ; 用rcx作为计数器,从1开始初始化 loop: ; 处理两位数(这里仅需处理10) cmp rcx, 10 jl print_single_digit ; 打印10的十位'1' mov rax, 1 mov rdi, 1 mov rsi, char_one mov rdx, 1 syscall ; 打印10的个位'0' mov rax, 1 mov rdi, 1 mov rsi, char_zero mov rdx, 1 syscall jmp update_counter print_single_digit: ; 把整数转换为ASCII字符 mov rax, rcx add rax, 0x30 mov [digit], rax ; 调用write打印字符 mov rax, 1 mov rdi, 1 mov rsi, digit mov rdx, 1 syscall update_counter: inc rcx cmp rcx, 11 ; 计数到11时停止(共打印1-10十次) jne loop exit: mov rax, 60 ; exit系统调用号(这部分你是对的) xor rdi, rdi ; 退出码0 syscall
代码关键修改说明
- 新增
.data段存储字符数据,给write提供合法的内存指针; - 改用
rcx作为计数寄存器,避免占用write需要的rsi指针寄存器; - 初始化计数器为1,消除未定义值的影响;
- 添加整数到ASCII的转换逻辑,确保打印可见数字;
- 单独处理10的两位数打印,符合预期输出;
- 修正
write的系统调用号为64位正确值1。
内容的提问来源于stack exchange,提问作者JaC
相关产品推荐
相关产品推荐

