C ++内联汇编运行时检查失败#0

时间:2013-11-21 20:01:52

标签: c++ assembly x86 inline-assembly

我正在研究一个需要内联汇编代码来反转字符串的c ++代码。 因此,如果我的输入是:“qwerasd”,则输出应为“dsarewq”。 我想过使用堆栈实现这个。我的代码是:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void reverseString(char* buffer, int len){

__asm {

    push ecx
    push edi
    push eax

    mov     ecx, len
    mov     edi, buffer

 top:
    mov     al, BYTE PTR [edi]
    push    al
    inc     edi
    loop    top

    mov     ecx, len
    mov     edi, buffer

refill:
    pop     al
    mov     BYTE PTR [edi], al
    inc     edi
    loop    refill
  }
}

int main()  {

    char    s[64];
    char    *ptr = s;
    int size;

    printf("\nEnter text: ");
    scanf("%s", s);
    size = strlen(s);

    reverseString(ptr, size);

    printf("\nThe new text is: %s\n\n", s);
    exit(0);
}

我试图逐个将字符推入堆栈,然后逐个弹出它们并将其存回字符串中。

当我运行代码时,我收到以下错误:      运行时检查失败#0 - ESP的值未在函数调用中正确保存。这通常是调用使用一个调用约定声明的函数和使用不同调用约定声明的函数指针的结果。

我做错了什么?

2 个答案:

答案 0 :(得分:3)

对于我来说,你想要用汇编语言来做这件事似乎有些荒谬,但无论如何。你的问题就在这里:

push ecx
push edi
push eax

你永远不会把那些从堆叠中弹出来!你放在堆叠上的所有东西,你必须在离开__asm { ... }区块之前再次取消。

请注意,可能没有必要保存所有这些寄存器。我对此并不是百分之百确定,但我认为Windows ABI说eaxecxedx调用被破坏的,这意味着您不必在像这样的叶子程序中保存和恢复它们。

答案 1 :(得分:3)

除了Zack的回答之外,推送edi实际上并不是必需的,因为看起来编译器会自动为您执行此操作,至少对于msvc而言。

上述代码的另一个问题是,您正在执行pop al,只会将esp增加一个字节。这显然会导致问题,因为正常的推送弹出操作预计在32位下进行4字节对齐。当你的函数退出时,它最终会为ebp恢复错误的值,它会返回一些错误的返回地址并随之发生混乱。

您可以这样修复:

top:
  movzx   eax, BYTE PTR [edi]
  push    eax
// ...
refill:
  pop     eax
  mov     BYTE PTR [edi], al

修改:为了添加一些说明,al 不是push pop上有效的操作数大小。不知道为什么msvc没有给出错误。上面的程序集最终会被转换为push eaxpop ax,而不是来自不平衡的地方。

相关问题