“ret”指令访问冲突

时间:2012-12-31 22:02:01

标签: c++ assembly access-violation inline-assembly

我有这个功能,主要由内联asm组成。

long *toarrayl(int members, ...){
    __asm{
        push esp

        mov eax, members
        imul eax, 4
        push eax
        call malloc
        mov edx, eax
        mov edi, eax

        xor ecx, ecx
        xor esi, esi
loopx:
        cmp ecx, members
        je done
        mov esi, 4

        imul esi, ecx
        add esi, ebp
        mov eax, [esi+0xC]
        mov [edi], eax
        inc ecx
        add edi, 4
        jmp loopx
done:
        mov eax, edx
        pop esp
        ret
    }
}

运行时,我在返回指令上遇到访问冲突。

我正在使用VC ++ 6,它有时可能意味着指向上面的行,所以可能在'pop esp'上。 如果你能帮助我,那就太好了。 谢谢,iDomo。

3 个答案:

答案 0 :(得分:8)

您无法正确管理堆栈指针。特别是,您对malloc的调用会导致堆栈失衡,而您的pop esp最终会将错误的值弹出到esp。因此,当您尝试从无效堆栈ret(CPU无法读取返回地址)时,会发生访问冲突。目前还不清楚为什么要推动和弹出esp;什么也没做。

答案 1 :(得分:0)

我自己想出了答案。

对于那些有同样或类似问题的人:

实际的异常是在用户代码之后发生的,当vc ++在调用函数之前自动弹出/恢复寄存器到它们的状态。由于我在调用malloc时错过了对齐堆栈指针,因此从堆栈弹出时存在访问冲突。我无法在编辑器中看到这个,因为它不是我的代码,因此它只显示为函数中的最后一个代码。

要解决此问题,只需在拨打电话后添加一个添加esp(前一次通话的参数大小)。

固定代码:

long *toarrayl(int members, ...){
    __asm{
        mov eax, members
        imul eax, 4
        push eax
        call malloc
        add esp, 4
        mov edx, eax
        mov edi, eax

        xor ecx, ecx
        xor esi, esi
loopx:
        cmp ecx, members
        je done
        mov esi, 4

        imul esi, ecx
        add esi, ebp
        mov eax, [esi+0xC]
        mov [edi], eax
        inc ecx
        add edi, 4
        jmp loopx
done:
        mov eax, edx
        ret
    }
    //return (long*)0;
}

优化代码:

long *toarrayl(int members, ...){
    __asm{
        mov eax, members
        shl eax, 2
        push eax
        call malloc
        add esp, 4
        ;cmp eax, 0
        ;je _error
        mov edi, eax
        mov ecx, members
        lea esi, [ebp+0xC]
loopx:
        mov edx, [esi]
        mov [edi], edx
        add edi, 4
        add esi, 4
        dec ecx
        jnz loopx
    }
}

答案 2 :(得分:0)

正如您所发现的,您永远不应该使用POP ESP指令 - 当您在代码中看到时,您知道发生了一些非常错误的事情。当然,在asseembler代码中调用malloc也是一件坏事 - 例如你忘了检查它是否返回NULL,所以你可能会崩溃。坚持在汇编程序之外 - 并检查NULL,调试更容易“无法在文件mycode.c中的第54行分配内存”而不是“汇编程序中的某处,我们得到了

这里有一些改进建议,可以加快你的循环:

long *toarrayl(int members, ...){
    __asm{
        mov eax, members
        imul eax, 4
        push eax
        call malloc
        add esp, 4
        mov edx, eax
        mov edi, eax

        mov ecx, members
        lea esi, [ebp+0xc]
loopx:
        mov eax, [esi]
        mov [edi], eax
        add edi, 4
        add esi, 4
        dec ecx
        jnz loopx
        mov lret, eax
        ret
    }
}

改进:在每个循环中删除乘以4。只需增加esi即可。在ecx上使用递减而不是递增,并在循环之前使用成员加载它。这允许在循环中使用一次跳转,而不是两次。从edx移除reduntant移动到eax。直接使用eax。