Levent Divilioglu
Levent Divilioglu

Reputation: 11612

To Pass The Content Of The Pointer In An Inline Assembly Function On Visual Studio

I want to understand the assembly language on Visual Studio using the __asm keyword in a c program.

What I try to do is; - Create an int array with 5 elements - Loop thru the array and add the values to the accumulator

Here is a code which is working fine;

#include <stdio.h>

int main()
{
    int intArr[5] = { 1, 2, 3, 4, 5 };
    int sum;
    char printFormat[] = "Sum=%i\n";

    __asm
    {
                lea esi, [intArr]       // get the address of the intArr
                mov ebx,5               // EBX is our loop counter, set to 5
                mov eax, 0              // EAX is where we add up the values
        label1: add eax, [esi]          // add the current number on the array to EAX
                add esi, 4              // increment pointer by 4 to next number in array
                dec ebx                 // decrement the loop counter
                jnz label1              // jump back to label1 if ebx is non-zero
                mov[sum],eax            // save the accumulated valu in memory
    }

    printf(printFormat, sum);

    return 0;
}

The output is as below;

Sum=15

I want to use the inline assembly part as a separate function, and do the same with a function call as below;

#include <stdio.h>

// function declaration
int addIntArray(int[], int);

int main()
{
    int     intArr[5] = { 1, 2, 3, 4, 5 };
    char    printFormat[] = "Sum=%i\n";
    int     result;

    result = addIntArray(intArr, 5);
    printf(printFormat, result);

    return 0;
}

int addIntArray(int intArr[], int size)
{
    int sum;

    __asm
    {
                lea esi, [intArr]       // get the address of the intArr
                mov ebx, 5              // EBX is our loop counter, set to 5
                mov eax, 0              // EAX is where we add up the values
        label1: add eax, [esi]          // add the current number on the array to EAX
                add esi, 4      // increment pointer by 4 to next number in array
                dec ebx                 // decrement the loop counter
                jnz label1              // jump back to label1 if ebx is non-zero
                mov[sum], eax           // save the accumulated value in memory
    }

    return sum;
}

The output is weird and as below;

Sum=2145099747

As I debug, I found that I just add up the address values which is stored in the esi register, instead of the contents of those addresses.

I am confused that, why the same inline assembly routine is working as I run it on the main thread, and why not working as I try to call it on a separate function.

Where is the problem, why is the process behaves different on main and function, and how can I fix it?

Upvotes: 2

Views: 1788

Answers (2)

Neitsa
Neitsa

Reputation: 8176

You are actually using the address of array that was passed on stack (that is, the address of the argument), not the address of the array itself.

This is actually easier (when it comes to assembly) if you use another debugger, like windbg.

Here's the code when addIntArray is called, everything's OK:

00b91010 c745e001000000  mov     dword ptr [ebp-20h],1 ; start filling array
00b91017 c745e402000000  mov     dword ptr [ebp-1Ch],2
00b9101e c745e803000000  mov     dword ptr [ebp-18h],3
00b91025 c745ec04000000  mov     dword ptr [ebp-14h],4
00b9102c c745f005000000  mov     dword ptr [ebp-10h],5
[...]
00b91044 6a05            push    5 ; pass number of elements in array
00b91046 8d55e0          lea     edx,[ebp-20h] ; load array address in edx
00b91049 52              push    edx ; pass edx to addIntArray
00b9104a e831000000      call    Tmp!addIntArray

Let's take a look at the stack when the CALL is made:

0:000> dd @esp L1
00fbfdb0  00fbfdbc

The above address is the address of the array, just display the content:

0:000> dd 00fbfdbc L5
00fbfdbc  00000001 00000002 00000003 00000004
00fbfdcc  00000005

Now let's take a look at addIntArray:

Tmp!addIntArray:
00b91080 55              push    ebp
00b91081 8bec            mov     ebp,esp
00b91083 83ec08          sub     esp,8
[...]
00b91090 53              push    ebx
00b91091 56              push    esi
00b91092 8d7508          lea     esi,[ebp+8] ; load what ???

So, what's in ebp+8?

0:000> dd @ebp+8 L1
00fbfdb0  00fbfdbc

This (0x00fbfdbc) is the address of the array, but as you are using LEA instead of MOV you are actually loading the address of ebp+8, not the address of the array.

Let's check the value of esi after the LEA has been executed:

0:000> r @esi
esi=00fbfdb0

This (0x00fbfdb0) is the address of the first argument, and it contains the address of the array, you're not using the array directly.

The command below dereferences the esi register:

0:000> dd poi(@esi) L5
00fbfdbc  00000001 00000002 00000003 00000004
00fbfdcc  00000005

So instead of using LEA, use MOV:

    ; ...
    mov esi, [intArr]       // get the address of the intArr
    mov ebx, 5              // EBX is our loop counter, set to 5
    mov eax, 0              // EAX is where we add up the values
    ; ...

Executing the program with a mov instead of an LEA now displays the expected value:

Sum=15

Upvotes: 7

Jester
Jester

Reputation: 58792

As your question title says, arrays are passed as pointers to functions. Thus, you need to treat it as a pointer: mov esi, [intArr] should work.

As illustration, consider this C code:

#include <stdio.h>

void func(int intArr[])
{
    printf("In func, sizeof(intArr) = %d\n", sizeof(intArr));
}

int main()
{
    int     intArr[5] = { 1, 2, 3, 4, 5 };

    printf("In main, sizeof(intArr) = %d\n", sizeof(intArr));
    func(intArr);

    return 0;
}

Sample output:

In main, sizeof(intArr) = 20
In func, sizeof(intArr) = 4

You can see what was an array in main is a pointer in func.

Upvotes: 5

Related Questions