Assembly print宏打印大数值时输出多余前缀字符问题排查
汇编print宏打印大数值前缀异常问题
问题表现
- 打印小数值时输出符合预期,打印大数值时输出开头多了额外字符,暂未定位根因
- 复现示例:
- 执行
print 1346269时输出为c1346269,开头多了字符c - 执行
print 40000时输出正常,为预期结果40000
- 执行
涉及代码
print宏实现:
%macro print 1 mov rax, %1 %%printInt: mov rcx, digit ; rcx指向digit缓冲区起始地址 mov rbx, 10 ; 存入换行符ASCII码 mov [rcx], rbx ; 缓冲区第一个字节存换行符 inc rcx ; 指针后移1字节 %%storeLoop: xor rdx, rdx ; 清空rdx,为除法做准备 mov rbx, 10 div rbx ; rax = rax / 10,rdx = rax % 10 add rdx, 48 ; 余数转对应ASCII字符 mov [rcx], dl ; 存入当前位字符 inc rcx ; 指针后移1字节 cmp rax, 0 jnz %%storeLoop ; rax不为0则继续处理下一位 %%printLoop: push rcx ; 调用sys_write输出1字节 mov rax, SYSWRITE mov rdi, 1 mov rsi, rcx mov rdx, 1 syscall pop rcx dec rcx cmp rcx, digit ; 判断是否已经输出到缓冲区开头的换行符 jge %%printLoop %endmacro
宏调用示例:
_start: print 40000 exit
根因分析
问题出在数位存储循环的指针处理逻辑:
- 在
%%storeLoop中,每次存储完当前位的字符后都会执行inc rcx移动指针,当处理完最高位(最后一个有效字符)时,rax已经为0,但代码依然会执行一次inc rcx,导致最终rcx指向最后一个有效字符后面的未初始化内存,而非最高位字符本身。 - 进入
%%printLoop后,第一次调用sys_write时rsi直接使用了这个指向未初始化内存的指针,输出的是数据段残留的垃圾值:- 打印40000这类小数值时,该位置残留的恰好是ASCII控制字符或空字符,终端不会显示,看起来输出正常
- 打印1346269这类大数值时,该位置残留的是可打印字符(本次是ASCII码0x63对应字符
c),就会出现前缀多余字符的异常
- 额外隐患:代码中未体现
digit缓冲区的定义大小,64位无符号整数最多有20位十进制数,加上末尾换行符,缓冲区至少需要21字节空间,否则打印极大值时会出现缓冲区越界写,破坏其他内存数据。
修复方案
在%%storeLoop结束、%%printLoop开始前,加一条dec rcx指令,让指针回退到最后一个有效字符(最高位数字)的位置,再开始循环打印即可。
修复后的核心逻辑片段:
cmp rax, 0 jnz %%storeLoop ; 数位存储完成后退出循环 dec rcx ; 回退指针,跳过存储最后一个字符后多余的自增 %%printLoop: push rcx ; 后续sys_write逻辑保持不变
内容的提问来源于stack exchange,提问作者user259137
相关产品推荐
相关产品推荐

