cdonts
cdonts

Reputation: 9599

Why passing 4-bytes arguments to a function expecting 2-bytes parameters works?

Check out this C code compiled with VC++ 2003/2005.

#include <stdio.h>
#include <windows.h>

void WINAPI SomeFunction(WORD a, WORD b)
{
    printf("%d + %d = %d\n", a, b, a + b);
}

int main()
{
    __asm {
        MOV EAX, 5
        MOV EBX, 6
        PUSH EBX
        PUSH EAX
        CALL SomeFunction
    }

    return 0;
}

In this ASM code, I'm passing two DWORD (4 bytes) arguments (by default as far as I know) to the SomeFunction() function that expects two WORD (2 bytes) arguments, and works perfectly (outputs 5 + 6 = 11).

1) Why this works?

Knowing that the function takes two WORD arguments, I'll do this:

__asm {
    MOV WORD PTR [EAX], 5
    MOV WORD PTR [EBX], 6
    PUSH EBX
    PUSH EAX
    CALL SomeFunction
}

While debugging it throws a Segmentation fault error.

2) Why this doesn't work?

Thanks in advance!

Upvotes: 2

Views: 320

Answers (1)

rodrigo
rodrigo

Reputation: 98348

The first one works because the Win32 ABI says that any argument with size less or equal 4 bytes is passed as 4 bytes, padding if necessary. So 16-bit words are actually passed as 32 bits. And that's what you are doing.

The second one does not work because it does a different thing:

MOV WORD PTR [EAX], 5

That line moves 5 into the 16-bit word starting at the memory location pointed to by EAX. But EAX is not loaded previously with a valid memory address. Moreover you are then pushing the pointer (a WORD*?).

In order to pass a 16-bit value in the stack you could use:

MOV AX, 5
MOV BX, 6
PUSH AX
PUSH BX

But that goes agains the Win32 ABI, as the stack is always 32-bit aligned.

Interestingly that will work if you pass this structure by value (untested):

struct WW
{
    WORD a, b;
};

void WINAPI SomeFunction(WW w)
{
    printf("%d + %d = %d\n", w.a, w.b, w.a + w.b);
}

int main()
{
    __asm {
        MOV BX, 6 // the parameters are reversed, methinks
        MOV AX, 5
        PUSH BX
        PUSH AX
        CALL SomeFunction
    }

    return 0;
}

That's because the fields of the structure are packed in 4 bytes (sizeof(WW)==4), so that's what is copied to the stack.

Naturally, playing with 16-bit registers is not fun. It is probably better to do:

MOV EAX 0x00060005
PUSH EAX

And copy the whole 32-bit struct in one go.

Upvotes: 3

Related Questions