簡體   English   中英

當我從C(與Visual Studio一起編譯)調用它時,為什么書中的這個16位DOS示例崩潰了?

[英]Why does this 16-bit DOS example from a book crash when I call it from C (compiled with visual studio?)

我的操作系統是Windows 7 64位。

這是我的代碼

first.c:

#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);
}

second.asm:

.model small
.code
public _second
_second proc near
    push bp
    mov bp,sp
    mov ax,[bp+4]
    mov bx,[bp+6]
    add ax,bx
    pop bp
    ret
_second endp
end

編譯正常,但是“ mov ax,[bp + 4] ”這一行出現錯誤“ 0xC0000005:訪問沖突讀取位置0x00000004”。

怎么了?

您正在以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位寄存器。 在尋址堆棧上的參數時,它使用ESI作為基數,而不是使用BP寄存器作為基數。 由於ESI沒有在函數中初始化為任何東西,因此它保留調用前碰巧具有的任何隨機值(例如0)。 凡是不是有效的可讀地址的地方,因此訪問它會導致崩潰。

您的問題是您已經使用了要與16位編譯器一起用於16位操作系統(例如MS-DOS)的匯編代碼,並使其與32位編譯器一起用於Windows。 您不能盲目剪切和粘貼這樣的代碼示例。 這是您的匯編代碼的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)則不需要。

在此特定情況下,SP和BP可能為0。 但是請注意,SP和BP分別是RSP和RBP的最低16位四分之一,因此堆棧指針實際上不是0。

將參數從.c傳遞到.asm的另一種方法是使用“ fastcall”約定,該約定使您可以在寄存器CXDX傳遞兩個參數(實際上是ECXEDX ,但在代碼中使用16位寄存器)。 接下來是在VS 2013中測試的簡短示例,它將兩個int(2,5)發送給asm函數,並且該函數返回這些值的相加(7):

第一.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;
}

第二個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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM