32位NASM汇编高位取反数组求和结果异常及编译警告问题
32位NASM汇编数组求和程序修正指南
核心问题定位
1. 求和逻辑错误点
- 初始版本:求和仅使用8位寄存器
bl,10个元素的最大求和结果远超8位寄存器上限,发生溢出;调用printf时仅传入16位bx,和%u要求的32位无符号参数长度不匹配,栈上残留垃圾值导致输出异常。 - UPD版本:错误将字节数组改为双字数组
dd,虽逻辑上可运行,但不符合字节元素的设计需求;未将单字节操作结果零扩展到32位寄存器,直接32位加法会引入非预期值。
2. 编译警告触发原因
当前gcc默认启用PIE(位置无关可执行文件)模式,代码中直接使用绝对地址访问.data段变量(如mov esi, arr)需要修改只读的.text段内容来填充地址,触发两类警告:
move to read-only section ".text"DT_TEXTREL is created in PIE
修正方案
代码逻辑修正
- 恢复字节数组
db定义,匹配无符号字节元素的设计需求 - 用32位寄存器
ebx存储求和结果,避免8位加法溢出 - 每次取字节元素后用
movzx将8位值零扩展到32位寄存器,再执行累加 - 严格匹配
printf的cdecl调用约定:参数从右往左压栈,调用后清理栈空间
编译警告消除
二选一即可:
- 编译链接时关闭PIE模式(最简单):使用
gcc -m32 -no-pie test.o命令 - 编写位置无关代码,适配PIE要求:访问全局变量时从GOT表读取地址,避免在
.text段写入绝对地址
修正后可运行代码
; 编译执行命令:nasm -felf32 test.asm && gcc -m32 -no-pie test.o && ./a.out section .rodata msg db 'Sum: %u', 10, 0 ; 只读格式串放到.rodata段 section .data arr db 0, 254, 126, 126, 126, 126, 126, 126, 126, 126 arr_len equ 10 section .text global main extern printf main: push ebp mov ebp, esp ; 建立标准C栈帧,保证调用规范 mov esi, arr mov ecx, arr_len xor ebx, ebx ; ebx存储求和结果,初始为0 sum_loop: mov al, byte [esi] test al, 10000000b ; 检测字节最高位 jz add_sum not al ; 最高位为1则对该字节取反 add_sum: movzx eax, al ; 将8位值零扩展到32位寄存器,避免溢出 add ebx, eax inc esi dec ecx jnz sum_loop ; 调用printf("Sum: %u\n", sum) push ebx ; 第二个参数:求和结果 push msg ; 第一个参数:格式串地址 call printf add esp, 8 ; 清理栈上参数,保持栈平衡 xor eax, eax ; main返回值为0 pop ebp ret
关键说明
注:原提问中预期结果计算存在小误差,8个126的累加值为1008,加上254取反后的1,最终正确求和结果为1009。
- 若需保留PIE模式(不使用
-no-pie参数),可将代码中访问数组的指令改为mov esi, arr wrt ..got,通过全局偏移表读取变量地址,生成位置无关代码,同样可以消除编译警告。 - 代码移除了未使用的
nextline变量、print宏,减少冗余。
内容的提问来源于stack exchange,提问作者Alex Breshniv
相关产品推荐
相关产品推荐

