You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

汇编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已由验证程序赋值

关键修正说明

  1. 栈帧修复:移除所有错误的栈操作,直接使用rdi传递的参数,遵循标准函数调用流程。
  2. 循环逻辑修正:将终止检查放到循环开头,确保遇到0字节立即退出,避免无限递增rdi。
  3. foo调用规范:先保存当前rdi地址(避免被foo覆盖),将单字节值零扩展到rdi作为参数,调用后恢复地址并写入结果。

内容的提问来源于stack exchange,提问作者tjwells

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 13:25:00