汇编str_lower函数内存错误排查:无限循环引发非法内存访问
问题分析与修正方案
核心错误点拆解
1. 函数调用的栈结构完全混乱
你在_start中执行push rdi; call str_lower,但call指令会自动将返回地址压入栈,此时栈结构为[返回地址][原始rdi值]。但str_lower开头的pop rbx会把返回地址弹到rbx,mov rdi, [rsp]才拿到原始rdi值——这彻底破坏了函数调用的栈帧,后续push rax; push rbx; ret的操作会导致返回后栈状态异常,程序执行流程完全失控。
同时,课程验证程序已经会给rdi赋值,你完全不需要手动push rdi传递参数,直接调用函数即可。
2. While循环终止逻辑颠倒
原Pascal代码的循环逻辑是:先检查当前字节是否为0,不为0才执行循环体,之后地址+1。但你的汇编代码中increment段的顺序错误:
increment: cmp byte ptr [rdi], 0 inc rdi jnz while
这会导致处理完最后一个非0字节后,地址+1指向终止符0,但jnz while的判断基于之前的非0字节,会再次进入循环,最终rdi持续递增到未映射内存,触发崩溃。
3. foo函数调用的参数传递错误
原Pascal中foo([src_addr])传递的是单字节值,但你用mov rcx, [rdi]读取8字节数据(rcx是64位寄存器),会越界读取内存。同时如果foo遵循x86-64 System V调用约定,参数应通过寄存器传递(第一个参数在rdi),而非栈传递。
修正后的汇编代码
.intel_syntax noprefix str_lower: xor rax, rax ; 初始化返回值i为0 test rdi, rdi ; 检查src_addr是否为0 jz return ; 为0直接返回 loop_start: cmp byte ptr [rdi], 0x00 ; 检查当前字节是否为终止符 jz return ; 是则退出循环 cmp byte ptr [rdi], 0x5A ; 判断是否为大写字母(<=0x5A) ja skip_conversion ; 不是则跳过转换 ; 调用foo函数转换为小写 push rdi ; 保存当前的src_addr(调用foo会覆盖rdi) movzx rdi, byte ptr [rdi] ; 将单字节零扩展到rdi作为参数 call 0x403000 ; foo返回结果存于rax pop rdi ; 恢复src_addr mov byte ptr [rdi], al ; 将转换后的字节写回内存 inc rax ; i +=1 skip_conversion: inc rdi ; src_addr +=1 jmp loop_start ; 回到循环开头 return: ret ; 返回rax中的i _start: call str_lower ; 直接调用函数,rdi已由验证程序赋值
关键修正说明
- 栈帧修复:移除所有错误的栈操作,直接使用rdi传递的参数,遵循标准函数调用流程。
- 循环逻辑修正:将终止检查放到循环开头,确保遇到0字节立即退出,避免无限递增rdi。
- foo调用规范:先保存当前rdi地址(避免被foo覆盖),将单字节值零扩展到rdi作为参数,调用后恢复地址并写入结果。
内容的提问来源于stack exchange,提问作者tjwells
相关产品推荐
相关产品推荐

