当我从C中调用它时,为什么这本书中的16位DOS示例崩溃了(使用visual studio编译?)

时间:2016-11-29 14:56:20

标签: c assembly visual-studio-2015 x86

我的操作系统是64位窗口。

这是我的代码

first.c:

$(".moving").not(this).animate(...

second.asm:

#include <stdio.h>

extern long second(int, int);

void main()
{
    int val1, val2;
    long result;

    scanf("%d %d", &val1, &val2);

    result = second(val1, val2);
    printf("%ld", result);
}

编译好了,但是“ mov ax,[bp + 4] ”此行有错误“0xC0000005:访问冲突读取位置0x00000004。”

出了什么问题?

3 个答案:

答案 0 :(得分:3)

您正在以16位模式汇编代码并将其链接到以32位模式执行的32位程序。组成second函数的机器代码最终会得到与预期不同的解释。这个代码实际执行了:

_second:
  00407800: 55                 push        ebp
  00407801: 8B EC              mov         ebp,esp
  00407803: 8B 46 04           mov         eax,dword ptr [esi+4]
  00407806: 8B 5E 06           mov         ebx,dword ptr [esi+6]
  00407809: 03 C3              add         eax,ebx
  0040780B: 5D                 pop         ebp
  0040780C: C3                 ret

代码不使用16位寄存器,而是使用32位寄存器。而是在寻址堆栈上的参数时使用BP寄存器作为基础,它使用ESI作为基础。由于ESI未在函数中初始化为任何内容,因此它保留了在调用之前发生的任何随机值(例如0)。无论在哪里都不是有效的可读地址,因此访问它会导致崩溃。

您的问题是您已经采用了用于16位编译器的汇编代码,用于16位操作系统(例如MS-DOS),并将其与32位编译器一起用于Windows。你不能盲目削减&amp;粘贴代码示例。这是32位版本的汇编代码:

    .MODEL FLAT
    .CODE

    PUBLIC _second
_second PROC
    push ebp
    mov  ebp, esp
    mov  eax, [ebp+8]
    mov  edx, [ebp+12]
    add  eax, edx
    pop  ebp
    ret
_second ENDP

    END

.MODEL FLAT指令告诉汇编器你正在生成32位代码。我已将代码更改为使用32位寄存器,并调整帧指针(EBP)相对偏移量以反映32位模式下的堆栈槽长为4个字节的事实。我还将代码更改为使用EDX而不是EBX,因为在32位C调用约定中,EBX寄存器需要由函数保留,而EDX(如16位C调用约定中的BX)则不需要。

答案 1 :(得分:0)

在这种特定情况下,SP和BP可能为0。但请注意,SP和BP分别是RSP和RBP的最低16位四分之一,因此堆栈指针实际上并非为0。

答案 2 :(得分:0)

将参数从 .c 传递到 .asm 的另一种解决方案是使用“fastcall”约定,它允许您在寄存器CX中传递两个参数和DX(实际上它是ECXEDX,但您在代码中使用了16位寄存器。接下来是在VS 2013中测试的一个简短示例,它向asm函数发送两个整数(2,5),函数返回这些值的加法(7):

<强> first.cpp

#include "stdafx.h"

extern "C" int __fastcall second(int,int); // ◄■■ KEYWORDS "C" AND __FASTCALL.

int _tmain(int argc, _TCHAR* argv[])
{   
    short int result = second(2,5); // ◄■■ "RESULT" = 7.
    return 0;
}

<强> second.asm

.model small
.code
public @second@8 ◄■■ NOTICE THE @ AND THE 8.
@second@8 proc near ◄■■ NOTICE THE @ AND THE 8.
    mov ax,cx ◄■■ AX = 2.
    add ax,dx ◄■■ AX + 5 (RETURN VALUE).
    ret
@second@8 endp ◄■■ NOTICE THE @ AND THE 8.
end