简体   繁体   中英

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

my OS is window 7 64-bit.

here is my code

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

compiled OK, but " mov ax,[bp+4] " this line has error "0xC0000005: Access violation reading location 0x00000004."

what's wrong?

You're assembling code in 16-bit mode and linking it into a 32-bit program which is executed in 32-bit mode. The machine code that makes up your second function ends up getting interpreted differently than you expected. This this code that is actually executed:

_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

Instead of using 16-bit registers the code uses 32-bit registers. Instead using the BP register as a base when addressing the arguments on the stack, it uses ESI as a base. Since ESI is not initialized to anything in the function, it holds whatever random value it happened to have before the call (eg. 0). Wherever that is isn't valid readable address so accessing it causes a crash.

Your problem is that you've taken assembly code meant to be used with a 16-bit compiler for a 16-bit operating operating system (eg. MS-DOS) and using it with a 32-bit compiler for Windows. You can't blindly cut & paste code examples like that. Here's 32-bit version of your assembly code:

    .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

The .MODEL FLAT directive tells the assembler you're generating 32-bit code. I've changed the code to use 32-bit registers, and adjusted the frame pointer (EBP) relative offsets to reflect the fact that stack slots in 32-bit mode are 4 bytes long. I also changed the code to use EDX instead of EBX because in 32-bit C calling convention the EBX register needs to preserved by the function, while EDX (like BX in the 16-bit C calling convention) doesn't.

SP and BP are probably 0 in this specific case. Note however that SP and BP are the lowest 16-bit quarters of RSP and RBP respectively, so the stack pointer isn't really 0.

Another solution to pass parameters from .c to .asm is to use the "fastcall" convention, which let you pass two parameters in registers CX and DX (actually it's ECX and EDX , but you are using 16 bit registers in your code). Next is a short example tested in VS 2013, it sends two ints (2, 5) to the asm function and the function returns the addition of those values (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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM